Interactive command. Authenticates the Fjall CLI and stores credentials in
~/.fjall/auth.json (or $FJALL_CONFIG_DIR/auth.json).Login is optional for local-only work, such as editing infrastructure files with
fjall add or scaffolding an application with fjall create app --offline. It is required for any command that talks to Fjall’s cloud services or to AWS.Usage
fjall login authenticates the CLI against your Fjall account and stores the credential locally. There are two paths:
- Device-code (the default): a browser window confirms the login.
- API key: paste a key from the dashboard, or pass
--api-key.
fjall org deploy reaches the member accounts it cascades to by assuming OrganizationAccountAccessRole from the management account’s session, and fjall unlock bucket --account reaches one through STS AssumeRoot from that session. When both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set, a command that derives a target’s credentials uses those keys instead (see Environment credentials override derivation). fjall unlock bucket, and the reads fjall domain delegate and fjall domain sync make in other accounts, never take them: they always sign in with the organisation’s own profiles. Fjall never writes ~/.aws/config or ~/.aws/credentials, but the AWS SDK can still read a profile from them, such as the one an exported AWS_PROFILE names (see The AWS SDK can still read your AWS profiles). aws sso login is never part of the flow.
Options
Authentication flow
Interactive login
E at this step. The CLI also falls back to API-key entry on its own if the device-code flow errors.
There is no method picker. Interactive login always starts with the device-code flow, and API-key entry is the fallback.
When a valid credential is already saved, the command asks first (a Yes/No selector defaulting to No):
--force.
Device-code login without the UI
Add--non-interactive to run the same flow as plain text, which is what CI logs and agent transcripts show:
--device on its own changes nothing. It routes to the same interactive screen, which already starts with the device-code flow. The flag matters in non-interactive and agent shells, where it selects the browser path over the API-key path.
Choosing an organisation
One credential file holds one API key, so the CLI is signed in to exactly one organisation at a time. Pass--org to pick which one the device flow signs you in to:
--orgrequires--device. With the API-key path it is refused, because a key authenticates its own organisation.--orgforces the non-interactive path. The interactive screen has no organisation input, so routing there would drop the flag.
--force, so a second organisation must never be reached that way on a machine where anything else is working.
To work across two organisations from one machine, give each one a state root of its own instead of re-logging in:
~ before the CLI sees it. The CLI does not: it resolves FJALL_CONFIG_DIR against the directory each command runs in, so an unexpanded ~/.fjall-acme becomes <that directory>/~/.fjall-acme. A login made that way is written there, API key included, usually inside your project, and a command run from any other directory finds no login.
So wherever else the work for that organisation runs, make sure the value is a full path by the time the CLI reads it:
- In the shell profile you use for it:
export FJALL_CONFIG_DIR="$HOME/.fjall-acme". The shell expands$HOMEinside double quotes, where it leaves~as it is. - In a project’s agent settings, so that every session in that project reads its own login: the absolute path, such as
/Users/you/.fjall-acme. No shell reads a settings file, so write the path out in full.echo ~/.fjall-acmeprints it.
fjall org get in any shell to see which organisation it acts for.
API-key login
Skip the browser entirely by passing a key:fj_. Anything else is rejected before the network call with Invalid API key format. Must start with 'fj_'.
Agent mode
How it works
- Loads any credential at
~/.fjall/auth.jsonand revalidates it against the Fjall API. - Requests a device code, or validates the supplied API key.
- Opens your browser and polls until you approve, up to the 15-minute device-code expiry.
- Saves the credential with
0600permissions inside a0700directory. - Discovers and records your organisation identity.
Credential storage
Fjall writes the credential to its state root:FJALL_CONFIG_DIR to relocate that root. It moves the whole root, credentials plus the connection, org-config and app-id caches, drift journals and CLI logs, so two organisations never share a coherent-looking mix of state.
The credential:
- Stays on your machine, never synced.
- Authenticates API requests, including the one that mints the token Fjall exchanges for an OIDC role’s AWS credentials.
- Carries restrictive file permissions (
0600). - Can be removed at any time.
The AWS SDK can still read your AWS profiles
Fjall never writes~/.aws/config or ~/.aws/credentials, and the credentials it derives for a target never come from them. It does pass AWS_PROFILE from your shell to the CDK CLI it runs, because your infrastructure.ts may read it. Where Fjall gives the AWS SDK no credentials of its own, the SDK looks for credentials itself, and can take them from a profile in those files, such as the one AWS_PROFILE names or the default profile.
fjall validate --deep --synth, for example, runs the CDK CLI with no credentials. Unless your shell sets both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, the CDK CLI then takes its credentials from a profile in those files when it finds one, and that profile’s account becomes the default account of the stacks the synth produces. Unset AWS_PROFILE first, or point it at a profile for the account you deploy to. With it unset, the CDK CLI can still take the default profile’s credentials.
The check of the CDK bootstrap stack that fjall deploy, fjall org deploy, fjall platform deploy and fjall account deploy make before they bootstrap their target account also gives the AWS SDK no credentials. With AWS_PROFILE set, the check reads the stack with that profile’s credentials, in the profile’s account, rather than with the credentials the deploy runs with. The bootstrap itself still runs with the deploy’s own credentials. Unset AWS_PROFILE before you deploy, or point it at a profile for the account you deploy to. With it unset, the check uses the deploy’s own credentials.
CI/CD integration
Device-code login needs a human at a browser and does not work unattended. Use a CI deploy token instead. Mint one in the web app under Settings → CI/CD Tokens, then see CI/CD Integration. Environment variable (recommended)FJALL_API_KEY before any stored credential, so no fjall login call is required.
Non-interactive login
--api-key or --device. The command errors otherwise. --device still needs a human to open the printed URL and approve within 15 minutes, so unattended pipelines should use --api-key or FJALL_API_KEY.
fjall login --api-key accepts legacy fj_ user keys only. It stores the
key as your credential. Scoped tokens (fjall_dk_ deploy tokens,
fjall_ak_ agent tokens) are not stored by fjall login. Pass them via
FJALL_API_KEY instead, which every command reads directly. For pipelines
prefer a deploy token. For AI agents acting as you, use an agent token from
fjall token create.Security
- Keep the
~/.fjall/directory private. - Generate a separate API key for each environment.
- Rotate keys on a regular schedule.
Removing credentials
Troubleshooting
Authentication failed
When the Fjall API refuses the key,fjall login saves nothing, and a login already saved in this state root stays as it was. The CLI prints the refusal the API sends, which for an fj_ key that is wrong, expired or revoked reads:
fjall login --api-key prints it below the summary’s ✗ Authentication login failed. The interactive screen shows Error: There is an issue with your API Key and asks for the key again.
The refusal is the same for all three, so it does not say which one applies. When
fj_ keys are no longer accepted for your organisation, the refusal says so instead and names its cure: a scoped agent token, set as FJALL_API_KEY.
A network failure is not a refusal. It prints its own message, such as Connection timeout to https://fjall.io. Please check your network connection. Check connectivity, then retry.
A TLS failure is not a refusal either. When the CLI cannot verify the certificate the Fjall API presents, it stops at the TLS handshake, before it sends the request, so your key never leaves the machine (measured on Node 22 and 24). It prints API request failed: followed by Node’s reason, such as unable to get local issuer certificate or certificate has expired. Behind a proxy your organisation runs that inspects TLS, whether it intercepts your traffic or you name it with HTTPS_PROXY, trust the proxy’s root certificate: set NODE_EXTRA_CA_CERTS to the path of its PEM file in the environment fjall starts in, then run the command again. Never set NODE_TLS_REJECT_UNAUTHORIZED=0 to get past it: the CLI would then send your key to whatever answered.
Common errors
Not authenticated
If commands report you are not authenticated:- Run
fjall loginagain. - Confirm
~/.fjall/auth.jsonexists. - Verify the file is readable by your user (
0600). - Check whether
FJALL_CONFIG_DIRis set in your shell, which moves the credential elsewhere. A value that is not an absolute path, such as an unexpanded~/.fjall-acme, is resolved against the directory each command runs in, so a login made from one directory is not found from another.
Shared machines
Each OS user has separate credentials under their own home directory, so there are no conflicts between users.Next Steps
Connect AWS
Link an AWS account before deploying.
Create an application
Scaffold an application and its infrastructure.
Deploy
Push your application to AWS.
Agent mode
Run the CLI as a coding agent with structured output.