# Configuration Guide This guide documents CLI flags, config files, credentials, keybindings, theme, and cache locations. ## CLI Flags Core flags: - `--config-dir` - `--cache-dir` - `--version`: print the build version and exit. - `--reset-config`: clear the resolved config, credentials, and cache files, rewrite current defaults, then exit without launching the TUI. - `--readonly`: read-only mode for this session. Overrides the `readOnly` config key when explicitly set (so `--readonly=false` can lift a read-only config for one session); the resolved value is never persisted. Fetch tuning flags: - `--page-size` - `--prefetch-pages`: per-resource detail window. - `--prefetch-ahead`: list rows beyond the cursor to prefetch, default `100`. - `--detail-fetchers` - `--rate` `--rate` overrides `fetch.ratePerSecond` and applies to each resolved credential+region detail-fetch bucket. `--detail-fetchers` remains the global concurrency cap for detail workers across all buckets. Credentials, region scopes, group scopes, theme, and keybindings are configured in the TUI or in YAML. There are no SSO start URL, SSO region, account, role, or region CLI flags. ## Files Configuration is split by sensitivity. `config.yaml` stores non-sensitive settings: - `version`: schema marker for the v3 scope model. - `credentials`: SDK profile, assume-role, SSO registration, and SSO role credential definitions. Provider values are `sdkProfile`, `assumeRole`, `ssoRegistration`, and `ssoRole`. - `regionScopes`: single-region browse targets, each pointing at a `credentialId`. - `groupScopes`: named collections of region scopes through `regionScopeIds`. - `scopeAliasReservations`: the shared first-come-first-served alias namespace for `regionScopes` and `groupScopes`. - `activeScope`: the persisted region/group scope plus resource type for resume. - `resourceTypeAliases`: command-palette aliases for CloudFormation resource types. New and `--reset-config` configs seed common aliases here; the persisted map is authoritative, so edit values to override defaults or remove keys to delete aliases. - `bulkWarnings`: defaults for each prefetch/export batch prompt. - `delete`: defaults for marked-delete batches. - `skin`: the selected color skin (see Theme). - `views`: per-resource-type table definitions (columns and sort). - `fetch`, `theme`, and `keybindings`. - `readOnly`: gate the AWS-mutating entry points (resource create, edit, delete) behind a "read-only mode" status notice. Browsing, export, and prefetch stay available — they never mutate AWS — and credential/scope/alias management is unaffected. The shell header shows an `[RO]` indicator next to the scope while active. `--readonly` overrides this key per session. An optional `themes/` directory next to these files holds user color skins, one `.yaml` per skin (see Theme). `credentials.yaml` stores sensitive SSO material: - OIDC client registration and tokens under `ssoRegistrations`, keyed by the `ssoRegistration` credential ID. When the file contains non-empty credentials, the payload is passphrase-encrypted with age and wrapped in a YAML envelope. If encrypted credentials exist, startup prompts for the passphrase before launching the TUI. Missing, empty, and zero-value credentials files do not prompt. The first SSO registration or authorization flow that would persist client secrets or tokens prompts the operator to choose and confirm the credentials passphrase in a modal that explains the passphrase protects sensitive material such as SSO client secrets and refresh tokens. The passphrase is kept in process memory only and is reused for later sensitive credential writes in that session. Non-empty plaintext `credentials.yaml` files are rejected; delete the file or run `a1s --reset-config` to start from current defaults. Role STS credentials, MFA token codes, and credential prompt responses are never persisted. Role credentials are resolved live when a region scope is used. The v3 scope model is authoritative. Older v1/v2 config files fail clearly and tell the operator to use `a1s --reset-config`. The app does not migrate old singleton `sso`/`context` fields or v2 context-object keys into v3 scope objects. Unrelated settings such as views, fetch, theme, and keybindings remain normal `config.yaml` settings only when they are already in a valid v3 file. Credential writes go through the `CredStore` seam and preserve mode `0600`. ## Locations By default: - Config lives under `os.UserConfigDir`. - Caches live under `os.UserCacheDir`, normally `~/.cache/a1s`. `--reset-config` respects `--config-dir` and `--cache-dir`. It removes the resolved a1s config root and cache root, recreates both, writes a materialized current-default `config.yaml`, and writes an empty `credentials.yaml` through the credential store so the file remains mode `0600`. Cache files are not seeded; type, scope, and prefetch cache entries regenerate lazily during normal use. The cache directory contains: - Global CloudFormation type descriptions from `internal/typecache`. - Scope discovery rows from `internal/scopecache`: SSO account/role rows keyed by SSO registration credential, enabled regions keyed by credential, and the global public CloudFormation resource type list. - Prefetch classification state from `internal/prefetchstate`. ## Resource Type Aliases `resourceTypeAliases` maps a command-palette token to a CloudFormation resource type. Aliases are lower-case, single-token strings and cannot shadow built-in commands such as `credentials`, `scope`, `aliases`, `alias`, `theme`, `skin`, or `q!`. New and reset configs start with these aliases: ```yaml resourceTypeAliases: buckets: AWS::S3::Bucket db-cluster-snapshots: AWS::RDS::DBClusterSnapshot db-clusters: AWS::RDS::DBCluster db-instances: AWS::RDS::DBInstance db-snapshots: AWS::RDS::DBSnapshot ddb: AWS::DynamoDB::Table dynamodb: AWS::DynamoDB::Table ebs: AWS::EC2::Volume ebs-snapshots: AWS::EC2::Snapshot ebs-volumes: AWS::EC2::Volume ec2: AWS::EC2::Instance ec2-instances: AWS::EC2::Instance ecr: AWS::ECR::Repository ecs: AWS::ECS::Cluster ecs-clusters: AWS::ECS::Cluster ecs-services: AWS::ECS::Service functions: AWS::Lambda::Function groups: AWS::IAM::Group igw: AWS::EC2::InternetGateway instances: AWS::EC2::Instance key-aliases: AWS::KMS::Alias kms: AWS::KMS::Key kms-aliases: AWS::KMS::Alias kms-keys: AWS::KMS::Key lambda: AWS::Lambda::Function log-groups: AWS::Logs::LogGroup logs: AWS::Logs::LogGroup policies: AWS::IAM::ManagedPolicy queues: AWS::SQS::Queue rds: AWS::RDS::DBInstance repos: AWS::ECR::Repository repositories: AWS::ECR::Repository roles: AWS::IAM::Role route-tables: AWS::EC2::RouteTable s3: AWS::S3::Bucket security-groups: AWS::EC2::SecurityGroup sg: AWS::EC2::SecurityGroup snapshots: AWS::EC2::Snapshot sns: AWS::SNS::Topic sqs: AWS::SQS::Queue subnets: AWS::EC2::Subnet tables: AWS::DynamoDB::Table task-defs: AWS::ECS::TaskDefinition topics: AWS::SNS::Topic users: AWS::IAM::User volumes: AWS::EC2::Volume vpcs: AWS::EC2::VPC ``` The `aliases` command opens the read-only table for this map. For now, edits are made directly in YAML or through the `alias []` command; eventually the aliases table can own direct editing. When the command is submitted from a resource table, the resource type can be omitted and defaults to the table's current type. ## Views `views` holds per-resource-type table definitions, mirroring the k9s `views.yaml` shape with CloudFormation type names as the resource keys: ```yaml views: AWS::S3::Bucket: columns: - Arn - tag:Team - Region sortColumn: Arn:asc AWS::EC2::Instance: columns: - InstanceType - tag:Name ``` - `columns` is the ordered display column selection saved by the column picker (`c`). Entries are CloudFormation property names, the synthetic origin metadata columns `AccountID`/`Region`, or tag columns (`tag:Key`, rendering the value of tag `Key` from the resource's `Tags` property). `Identifier` is implicit and always first; it is never persisted. - `sortColumn` persists the table's local sort as `Column:asc` or `Column:desc` (the direction is the last `:`-separated segment, so a tag column composes as `tag:Key:desc`). Absent means arrival order. An unknown or unselected column is ignored rather than an error. The digit keys `1`-`0` write this field as the sort cycles; it is restored when the type's table next opens. A type with no `views` entry uses the built-in default columns in arrival order. The former bare `columns:` map is gone (pre-release schema break); an old `columns:` key in `config.yaml` is ignored on load. ## Metrics The `M` metrics pane's chart presets come from a built-in catalog that the optional `metrics:` section extends or overrides. Two sub-maps: ```yaml metrics: views: AWS::Lambda::Function: - name: default namespace: AWS/Lambda charts: - {label: Invocations, metric: Invocations, stat: Sum, dimensions: {FunctionName: "{identifier}"}} custom: payments-health: range: 24h charts: - {label: API 5XX, namespace: AWS/ApiGateway, metric: 5XXError, stat: Sum, dimensions: {ApiName: payments-api}} ``` - `metrics.views` maps a CloudFormation type to a list of NAMED views, the same shape as the built-ins (which ship one view per type named `default`). Merge is by `(type, name)`: a user view with a built-in's name replaces it wholly; new names append; an empty `charts: []` suppresses a view. The section is optional — absent means built-ins only. - `metrics.custom` declares free-standing CloudWatch views not tied to a row: literal dimension values, opened with `:metrics ` from the command palette (bare `:metrics` lists the configured names). Custom views resolve against the active REGION scope; group scopes refuse with a status hint (the pane is one batched `GetMetricData` call per view). - When a type has more than one view, the pane header shows `view: (i/n)` and `tab`/`shift+tab` cycle views (each cycle is a fresh load). - Dimension values are literals with `{token}` substitution. Row tokens: `{identifier}`, `{queue-name}`, `{arn-final-segment}`, `{ecs-cluster}`, `{ecs-service}`, `{alb-id}`. Scope tokens: `{region}`, `{accountId}`. A token the row/scope cannot supply drops that chart (it is never queried with a bogus dimension). There are no property paths or expressions. - Validation runs at startup beside the keybindings warnings: an invalid stat/period/token drops the chart with a warning; more than 4 charts in a view truncates. A misspelled metric/namespace is NOT detectable locally — CloudWatch returns empty series, so a typo renders an empty chart. - The built-in catalog is seeded into `config.yaml` at first launch and by `--reset-config`, so the defaults are visible and editable in place. Seeding never overwrites an existing file; an already-seeded config does not pick up newer built-ins until `--reset-config` re-syncs it (or the section is hand-edited). - `metrics` is a reserved command-palette token; a resource-type alias may not shadow it. ## Fetch Settings `internal/config` maps config and flag values into a fetch config. Invalid or zero config values fall back to defaults. CLI flags override config only when the flag was explicitly set. Preserve the zero-means-default behavior unless a change intentionally migrates the config format. ## Batch Defaults `bulkWarnings` seeds each prefetch/export batch: - `dismissedDefault`: whether the prompt starts dismissed for that batch. - `initialThreshold`: first fetched-resource count that pauses for warning. - `thresholdMultiplier`: multiplier for repeated warning thresholds. The prompt checkbox applies to the current batch only. It does not rewrite the YAML default. `delete` seeds delete batches: - `parallelChunkSize`: maximum delete requests started in one chunk. - `ignoreFurtherErrorsDefault`: default state of the delete-error prompt's ignore-further-errors checkbox. The delete-error checkbox applies to the current delete batch only. ## Theme Colors resolve in three layers, each overriding the previous one field-wise: 1. Built-in defaults (exactly the `default` skin). 2. The skin selected by the top-level `skin:` field. 3. The inline `theme:` block. Color values anywhere in the stack accept: - ANSI16 index strings `"0"`–`"15"` (the terminal's own 4-bit palette), - ANSI256 index strings `"16"`–`"255"`, - hex `"#rrggbb"` or `"#rgb"`, - `"default"` — the terminal default foreground/background (Lip Gloss `NoColor`: foregrounds use the terminal text color, backgrounds are not drawn). Lip Gloss degrades hex/256-color values per terminal profile, so wider values cannot break ANSI-only terminals. Unset or invalid color fields keep the layer below — a partial or stale theme can only widen, never break, the defaults, and golden tests remain stable. Theme resolution happens at model construction, before `tea.NewProgram`; the `:theme` command rebuilds it live (below). ### Skins `skin: ` selects a named skin, k9s-style: a `/themes/.yaml` document, else a built-in of that name. A user file shadows a same-named built-in; a malformed user file disables the skin layer rather than silently falling back. Unknown names are ignored at startup. Built-ins: `default` (today's palette, byte-identical to no skin at all), `catppuccin-latte` (light), and `catppuccin-mocha` (dark). A skin file may set any of the full role set (every key optional): ```yaml # /themes/corp.yaml accent: "#cba6f7" # primary highlight: selected scope, prompts, titles dim: "240" # muted text, separators, footers err: "9" # error text crumb: "8" # breadcrumb text... crumbBg: "11" # ...over this background headerLabel: "12" # header field labels headerValue: "15" # header field values hotkeyLabel: "12" # hotkey help: the key hotkeyDesc: "15" # hotkey help: the description titlePunct: "14" # body title punctuation ( ) [ ] titleScope: "13" # body title scope name titleCount: "15" # body title row count filterFrame: "15" # filter prompt border + filter badge background logo: "11" # the header logo tableBorder: "4" # body/table/modal borders paletteBorder: "14" # command prompt border diffAdd: "10" # added lines in the update diff diffDel: "9" # removed lines in the update diff progressFill: "13" # export progress bar fill ``` ### Inline theme block `theme:` in `config.yaml` is the final override layer over the skin and supports the same four fields it always has — `accent`, `dim`, `err`, `crumb` — now in the full color vocabulary above. Existing ANSI16 configs render byte-identically. ```yaml skin: catppuccin-mocha theme: accent: "#ff8800" # everything else stays mocha ``` ### Per-scope accents `accent:` on a credential, region scope, or group scope overrides the theme accent while that scope is the active target — a visual guard rail, e.g. red for production. A region scope's own accent wins over its credential's; a group scope applies only its own. Deactivation restores the theme accent. Invalid values are ignored. ```yaml credentials: - id: prod-admin accent: "#d20f39" # any scope over this credential goes red regionScopes: - id: prod-west credentialId: prod-admin accent: "208" # ...except this one, which goes orange ``` `:accent ` sets the active target's own accent from the command palette without editing config — the same forms config accepts (an ANSI index like `208` or hex like `#d20f39`) — applies it live, and persists it to the scope's `accent:` field. `:accent none` clears it, falling back to the credential's accent (region scopes) or the theme accent; the bare command shows the target's current accent. Unlike config-file entry, an invalid color is rejected with a status notice rather than silently ignored. ### Live switching `:theme ` (alias `:skin`) switches the skin live from the command palette and persists the choice to the `skin:` field. The bare command lists the available skins — built-ins plus the `themes/` directory — and the name argument tab-completes from the same set. An unknown name reports the available skins and changes nothing. The inline `theme:` block and an active per-scope accent keep overriding the newly selected skin. ## Keybindings Default global keys: - `:` command palette for built-ins, resource types, and resource-type aliases. - `ctrl+r` refresh. - `c` columns. - `1`-`0` sort by the addressed visible column (3-state cycle: ascending, descending, cleared to arrival order). - `E` grouped listing errors. - `shift+p` prefetch all loaded/remaining resource pages. - `ctrl+s` save/export. - `+` create. - `e` edit. - `ctrl+d` delete. - `shift+t` bulk tag editor over marked rows (else the selected row). - `enter` select. - `esc` back. - `?` help. - `ctrl+c` hard-quits. It is intentionally not listed as footer help. Help placement is split by surface. When browsing tables, stable navigation and selection keys, `:` command palette, mark/range/clear-mark keys, and `?` help render in the footer. Contextual actions such as refresh, columns, prefetch, save/export, create, edit, delete, YAML, credential creation, scope creation, rename, and SSO register/authorize render in the header. Quitting from the command palette is the `q!` command. Plain `q` is not a hotkey. Credential and scope table keys: - `credentials` command: `a` assumed role, `s` SDK credential, `r` SSO registration credential, `o` SSO role credential batch flow, `enter` opens the selected SSO registration modal, `ctrl+d` delete, `shift+r` rename alias. - `scope` and `scopes` commands open the scope table: `r` creates region scopes, `g` creates a group scope, `ctrl+d` deletes, and `shift+r` renames aliases. `context` and `ctx` are not built-in commands. - `aliases` opens the resource-type alias table (one row per alias): `+` adds, `enter` edits the selected alias (token and/or type), `ctrl+d` removes behind a confirm. `alias` still creates resource-type aliases from the command palette, and `alias ` defaults to the current resource type when submitted from a resource table. - Resource tables use `y` to open the selected row as YAML/detail. Resource, credential, scope, and picker tables still use `space`, `ctrl+space`, and `ctrl+\` for marking where the workflow supports marked rows. SSO registration creation saves the credential definition, opens the registration modal, and immediately attempts first registration and authorization. If no credential-encryption passphrase is available yet, the app opens an encryption modal before registration or authorization starts. SSO registration modals persist and show the client ID plus registration and authorization status/expirations as the device-auth flow progresses. `r` registers or reregisters the OIDC client, and `a` authorizes the registration. Authorization tries an existing refresh token before starting a new device-code flow. When device authorization is needed, the authorization URL and waiting spinner are shown inside the modal and clear after authorization succeeds. Mark one or more SSO registration credentials in `credentials`, then press `o` to enumerate account/role rows. Selected `ssoRole` rows can be renamed with `shift+r` before creation. Region scope creation mirrors the SSO role batch flow: choose or mark one or more credentials first, then choose or mark enabled AWS regions. The region list is the union of enabled and default-enabled regions reported by the selected credentials. Creation writes one `regionScope` for each selected region and supporting credential pair. Multiple created scopes also create a `groupScope` over them. `c`, `E`, `shift+p`, `ctrl+s`, `+`, `shift+t`, `!` (SSM session, `AWS::EC2::Instance` rows only), and the `1`-`0` sort digits are list-only. `e` and `ctrl+d` work on list and detail. Single-character hotkeys are gated behind `textEntryFocused()` so they do not fire while typing. `keyMap.withOverrides(cfg.Keybindings)` overlays an action-name-to-keys map onto defaults. The rebindable action vocabulary is single-sourced as `rebindableActions` in `keys.go`, and includes only actions whose dispatch consults the key map through `key.Matches`. Deliberately not rebindable: - `up` and `down`, which are owned by bubbles components. - `select` and `back`, which are literal enter/escape behavior. - Credential/scope-table local keys such as `ctrl+d`, `shift+r`, `a`, `s`, `o`, `r`, and `g`. Unknown actions and empty lists are ignored by application, but `validateKeybindings` prints advisory startup warnings. Bubble Tea v2 `Key.String()` returns the shifted character, so `shift+r` is handled internally as `R` and can be configured as `"R"` for rebindable actions. Context-table help renders the local rename action as `shift+r`. ## Sensitive Data Boundary Keep sensitive data out of `Config`. SSO tokens and client secrets belong in age-encrypted `Creds`; role STS credentials should not be persisted at all. When adding new settings, decide whether the value is sensitive before choosing the file. If it can authorize AWS access or contains a token or secret, it does not belong in `config.yaml`.