Skip to main content
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.
For the management account, and for any other account that carries its own OIDC role, the Fjall credential is the only one you supply: Fjall exchanges a token the Fjall API mints from it for that role’s AWS credentials. A command run against an organisation member account with no OIDC role of its own gets that account’s credentials from AWS IAM Identity Center instead, through a sign-in you make in your browser (see SSO re-authentication). 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

The command loads any saved credential and revalidates it against the Fjall API. When no valid credential exists it starts the browser device-code flow straight away, opens your browser, and waits:
Approve in the browser to finish. To enter an API key instead, press 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):
Answer No to exit, Yes to continue into the device-code flow. This prompt appears whether or not you pass --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:
In a terminal, --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:
Two rules apply:
  • --org requires --device. With the API-key path it is refused, because a key authenticates its own organisation.
  • --org forces the non-interactive path. The interactive screen has no organisation input, so routing there would drop the flag.
Every shell and agent session that reads a state root uses the login it holds. Signing in again on that root replaces that login for all of them at once, whether you answer Yes at the overwrite prompt or pass --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:
On a command line the shell expands an unquoted ~ 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 $HOME inside 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-acme prints it.
Run fjall org get in any shell to see which organisation it acts for.

API-key login

Skip the browser entirely by passing a key:
Generate API keys from the Fjall dashboard. The key must start with fj_. Anything else is rejected before the network call with Invalid API key format. Must start with 'fj_'.

Agent mode

Agent mode emits a structured browser hand-off block carrying the verification URL, the user code, the expiry and the poll interval, then a result block on completion. See Agent mode.

How it works

  1. Loads any credential at ~/.fjall/auth.json and revalidates it against the Fjall API.
  2. Requests a device code, or validates the supplied API key.
  3. Opens your browser and polls until you approve, up to the 15-minute device-code expiry.
  4. Saves the credential with 0600 permissions inside a 0700 directory.
  5. Discovers and records your organisation identity.

Credential storage

Fjall writes the credential to its state root:
Set 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)
The CLI reads FJALL_API_KEY before any stored credential, so no fjall login call is required. Non-interactive login
In non-interactive mode you must pass --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 login again.
  • Confirm ~/.fjall/auth.json exists.
  • Verify the file is readable by your user (0600).
  • Check whether FJALL_CONFIG_DIR is 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.