You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
git-fi's branch listing has two modes. Without a GitLab token it prints branch names. With one it prints each branch's pipeline status, author, and date, plus clickable links into GitLab, and it reports the fi pipeline a merge just triggered. Today the only way to enable that is exporting GITLAB_ACCESS_TOKEN in your shell.
An environment variable rewards the expedient choice over the secure one. The cheapest way to satisfy it is reusing a token that is already exported for something else, which is typically full api scope, when git-fi only needs read_api. Nothing in the current path asks for a narrower token or tells you when you have supplied a broad one.
The consumer is any developer who wants pipeline status in git fi, which is the whole audience for the feature.
Proposed approach
Add stored credentials, and keep the environment variable working.
Resolution order: stored config first, then GITLAB_ACCESS_TOKEN, except in CI where the variable wins. The principle is to prefer the deliberate credential over the ambient one. On a developer machine that is the stored token, which should win over whatever happens to be exported. In CI it is the job's variable, so when CI is set that comes first, and a config file baked into a container image cannot silently shadow it. Either way an existing export keeps working, so this lands as an addition rather than a migration.
Surface: an --auth[=login|status|logout] action flag, not an auth subcommand.
Why not git fi auth login, the shape gh and glab use? Positional arguments to git fi are branch names, and a lone positional becomes the list filter, so auth would have to become a reserved word and git fi auth would stop filtering for auth-related branches. The existing help and install-completions subcommands get away with it because nobody filters on those words. Carrying the verb as a flag value keeps every positional meaning "branch".
Bare --auth reports status, --auth=login prompts and stores, --auth=logout removes. This would be the first long-only flag in a table where everything has a short form, which suits a command run once per machine.
login inspects the token it was given. After reading the token it calls GET /personal_access_tokens/self, which returns the token's scopes and expires_at, and warns when the token carries more than read_api. This is what addresses the problem rather than restating it in documentation: the tool tells you when you have just handed it a broadly-scoped token.
The prompt makes the narrow token the low-effort path by linking the prefilled token form, which GitLab populates from query parameters:
Requirements land in SPEC.md as AUTH-* ids before any implementation, since language ports are being explored and each one needs the same resolution order, file permissions, and redaction rules.
Acceptance criteria
SPEC.md carries AUTH-* requirements for resolution order, storage location and permissions, the stdin-only rule, scope validation, and status redaction
A stored token takes precedence over GITLAB_ACCESS_TOKEN, and an existing export keeps working when nothing is stored
When CI is set, GITLAB_ACCESS_TOKEN takes precedence over any stored token
Bare --auth names which source the live token came from, and says when a stored token is shadowing an exported one
--auth=login reads the token from stdin only, never from an argv value, since argv is visible in ps and lands in shell history
The token is stored at $XDG_CONFIG_HOME/git-fi/config.json with the directory at 0700 and the file at 0600
git-fi refuses to read that file when it is group- or world-readable, and says why
config.json carries a schemaVersion
--auth=login validates the token against GET /personal_access_tokens/self and warns when its scopes exceed read_api
The login prompt links the prefilled token form for the detected host
Bare --auth prints host, scopes, expiry, and the last 4 characters of the token, and never the value
--auth=logout removes the stored token
Completion offers login, status, and logout as values for --auth
Considerations
The permission check is the point. A 0600 file is only an improvement over an export because fewer processes can read it. Without the refuse-on-loose-permissions check it is just a differently-shaped plaintext secret, so that check is load-bearing rather than a nicety.
An OS keyring is out of scope. macOS security, libsecret, and Windows DPAPI are three platform paths and a real burden for every port to reimplement, which is too much for a read-only token. The schemaVersion field exists so a later move to a keyring can discard the plaintext generation rather than leave it behind.
Worth weighing: store nothing at all. When glab is installed, git-fi could shell out to glab api and hold no credential of its own, which is the only option where git-fi never touches a secret. The cost is depending on another tool being present for a feature that currently works standalone. Deciding this before writing the AUTH-* requirements would avoid specifying storage twice.
No existing leak path to fix first. The token already travels as a PRIVATE-TOKEN header (src/gitlab.ts:56) rather than in a URL, and --debug prints only git commands, so this work adds a storage mechanism without having to clean up the transport.
Context
git-fi's branch listing has two modes. Without a GitLab token it prints branch names. With one it prints each branch's pipeline status, author, and date, plus clickable links into GitLab, and it reports the
fipipeline a merge just triggered. Today the only way to enable that is exportingGITLAB_ACCESS_TOKENin your shell.An environment variable rewards the expedient choice over the secure one. The cheapest way to satisfy it is reusing a token that is already exported for something else, which is typically full
apiscope, when git-fi only needsread_api. Nothing in the current path asks for a narrower token or tells you when you have supplied a broad one.The consumer is any developer who wants pipeline status in
git fi, which is the whole audience for the feature.Proposed approach
Add stored credentials, and keep the environment variable working.
Resolution order: stored config first, then
GITLAB_ACCESS_TOKEN, except in CI where the variable wins. The principle is to prefer the deliberate credential over the ambient one. On a developer machine that is the stored token, which should win over whatever happens to be exported. In CI it is the job's variable, so whenCIis set that comes first, and a config file baked into a container image cannot silently shadow it. Either way an existing export keeps working, so this lands as an addition rather than a migration.Surface: an
--auth[=login|status|logout]action flag, not anauthsubcommand.Bare
--authreports status,--auth=loginprompts and stores,--auth=logoutremoves. This would be the first long-only flag in a table where everything has a short form, which suits a command run once per machine.logininspects the token it was given. After reading the token it callsGET /personal_access_tokens/self, which returns the token'sscopesandexpires_at, and warns when the token carries more thanread_api. This is what addresses the problem rather than restating it in documentation: the tool tells you when you have just handed it a broadly-scoped token.The prompt makes the narrow token the low-effort path by linking the prefilled token form, which GitLab populates from query parameters:
Requirements land in
SPEC.mdasAUTH-*ids before any implementation, since language ports are being explored and each one needs the same resolution order, file permissions, and redaction rules.Acceptance criteria
SPEC.mdcarriesAUTH-*requirements for resolution order, storage location and permissions, the stdin-only rule, scope validation, and status redactionGITLAB_ACCESS_TOKEN, and an existing export keeps working when nothing is storedCIis set,GITLAB_ACCESS_TOKENtakes precedence over any stored token--authnames which source the live token came from, and says when a stored token is shadowing an exported one--auth=loginreads the token from stdin only, never from an argv value, since argv is visible inpsand lands in shell history$XDG_CONFIG_HOME/git-fi/config.jsonwith the directory at0700and the file at0600config.jsoncarries aschemaVersion--auth=loginvalidates the token againstGET /personal_access_tokens/selfand warns when its scopes exceedread_api--authprints host, scopes, expiry, and the last 4 characters of the token, and never the value--auth=logoutremoves the stored tokenlogin,status, andlogoutas values for--authConsiderations
The permission check is the point. A
0600file is only an improvement over an export because fewer processes can read it. Without the refuse-on-loose-permissions check it is just a differently-shaped plaintext secret, so that check is load-bearing rather than a nicety.An OS keyring is out of scope. macOS
security, libsecret, and Windows DPAPI are three platform paths and a real burden for every port to reimplement, which is too much for a read-only token. TheschemaVersionfield exists so a later move to a keyring can discard the plaintext generation rather than leave it behind.Worth weighing: store nothing at all. When
glabis installed, git-fi could shell out toglab apiand hold no credential of its own, which is the only option where git-fi never touches a secret. The cost is depending on another tool being present for a feature that currently works standalone. Deciding this before writing theAUTH-*requirements would avoid specifying storage twice.No existing leak path to fix first. The token already travels as a
PRIVATE-TOKENheader (src/gitlab.ts:56) rather than in a URL, and--debugprints only git commands, so this work adds a storage mechanism without having to clean up the transport.