Skip to content

Configuration Reference

FinFocus is configured via a configuration file (default: ~/.finfocus/config.hujson) and environment variables.

The configuration file is HuJSON: JSON with comments and trailing commas. config set and the dashboard Config page write config.hujson. A legacy config.yaml is still read. The next save of that tier writes the HuJSON sibling and leaves the YAML file in place. Examples below show the keys; the on-disk file uses JSON syntax.

output:
default_format: table # table, json, ndjson
precision: 2
logging:
level: info # debug, info, warn, error
plugin_dir: ~/.finfocus/plugins

FinFocus uses a two-tier configuration system where project-local settings override user-global defaults.

Tier Location Purpose
Project-local $PULUMI_PROJECT/.finfocus/config.hujson Project-specific budgets, output preferences, plugin config
User-global ~/.finfocus/config.hujson Shared defaults across all projects

When a Pulumi project is detected, FinFocus creates a .finfocus/ directory alongside Pulumi.yaml:

my-pulumi-project/
├── Pulumi.yaml
├── .finfocus/
│ ├── .gitignore # Auto-generated, protects user-specific data
│ ├── config.hujson # Project budgets, output prefs
│ └── dismissed.json # Per-project recommendation dismissals

Project-specific settings (config, dismissals) are resolved in this order:

  1. --project-dir flag (explicit override)
  2. FINFOCUS_PROJECT_DIR environment variable
  3. Walk up from CWD to find Pulumi.yaml, then use $PROJECT/.finfocus/
  4. Fall back to ~/.finfocus/ (backward compatible)

Global resources (plugins, cache, logs) are resolved separately:

  1. FINFOCUS_HOME environment variable
  2. PULUMI_HOME/finfocus
  3. ~/.finfocus/

Project config.hujson overrides global config.hujson at the top-level key level (shallow merge). Keys absent in the project config inherit from global defaults.

For example, if the global config defines output and logging sections, and the project config only defines output, the project output section replaces the global one entirely while logging is inherited from the global config.

# ~/.finfocus/config.hujson (global)
output:
default_format: table
precision: 2
logging:
level: info
# my-project/.finfocus/config.hujson (project)
output:
default_format: json
precision: 4
# Effective config: output from project, logging from global
  • default_format: The default output format for commands.
  • precision: Number of decimal places for cost values.
  • level: The verbosity of logs.
  • plugin_dir: Top-level key that overrides the computed plugin installation directory. Equivalent to setting the FINFOCUS_PLUGIN_DIR environment variable (env var takes precedence). Example: plugin_dir: /opt/finfocus/plugins

Pulumi ESC can supply plugin process environment variables. The section is top-level. It does not live under plugins.<name>, because that object is injected into the plugin process.

{
"esc": {
"environments": ["billing/aws-readonly@stable"],
"plugins": {
"vantage": {
"environments": ["billing/vantage"],
"variables": ["VANTAGE_TOKEN"]
}
}
}
}
Key Kind Meaning
esc.environments String list References used for every plugin. Replaces the stack’s ESC imports.
esc.plugins.<name>.environments String list References for that plugin. Replaces esc.environments and the stack imports.
esc.plugins.<name>.variables String list Extra variable names that plugin may receive from ESC.

A missing environments key means “no preference”: finfocus uses the stack imports. An empty list ([]) turns ESC off for what that key covers. A per-plugin empty list disables ESC for that plugin only. An empty global list disables ESC for every plugin that does not set its own list.

A project esc section replaces the global esc section as a whole, the same way every other top-level section merges. Keys under esc are not secrets. config get and the Config page show the references and the variable names.

config set takes a comma-separated list:

Terminal window
finfocus config set esc.environments 'billing/aws-readonly@stable,shared/tags'
finfocus config set esc.plugins.vantage.variables 'VANTAGE_TOKEN'
finfocus config set esc.environments ''

The empty string writes [], which disables ESC. There is no unset command. Delete the key from the file, or remove it on the Config page, to return to “not set”.

Every command except the config commands rejects a bad value with exit code 2 before a plugin starts. config validate reports it. Validation does not contact Pulumi Cloud.

Problem Message
Malformed reference esc.environments[0]: invalid ESC environment reference "a//b": empty segment
Bad variable name esc.plugins.vantage.variables[1]: invalid environment variable name "1BAD"
Reserved variable esc.plugins.vantage.variables[0]: FINFOCUS_PLUGIN_PORT is reserved for plugin startup
Unknown plugin esc.plugins.foo: no plugin named "foo" is installed (warning)
Wrong JSON type The usual type-mismatch error for esc.environments

config set still accepts a replacement while the file contains a bad reference, so the value can be repaired. config validate keeps reporting that error until the file is valid. See Pulumi ESC credentials.

Configure the BoltDB-backed cost calculation cache.

  • enabled: Master switch for caching. Set to true to enable. Default: false.
  • ttl_seconds: Seconds until a cached entry expires. 0 disables expiration. Default: 3600 (1 hour).
  • directory: Explicit path for the cache database file. When empty, auto-resolves to the project .finfocus/ directory or ~/.finfocus/cache/.
  • lru_enabled: Put an in-memory LRU in front of BoltDB. Default: false. FINFOCUS_CACHE_LRU_ENABLED overrides this value.
  • lru_max_items: How many entries the memory tier keeps. 0 uses 256. FINFOCUS_CACHE_LRU_MAX_ITEMS overrides this value. A negative value is ignored.

Configure the optional recommendation scoring step. It is off by default and adds no network activity of its own; the scorer is a plugin. See the Recommendation Scoring Guide for behavior and data handling.

  • scoring.enabled: Turn scoring on. Default: false.
  • scoring.plugin: Name of the scorer plugin. Required when enabled.
  • scoring.identifier_mode: How resource ids and names reach the scorer: pseudonymized (default), omitted or raw.
  • scoring.field_allowlist: Recommendation fields sent to the scorer. Empty sends all. Valid names: category, action_type, resource, impact, priority, confidence_score, description, reasoning, source, created_at, metadata, action_detail, primary_reason, secondary_reasons, pricing_category, spot_interruption_risk_score.
  • scoring.timeout_seconds: Timeout for each scorer call. Default: 30.
  • scoring.needs_review.risk, scoring.needs_review.false_positive, scoring.needs_review.insufficient_evidence: Thresholds (0 to 1) for the needs-review marker. Defaults: 0.3, 0.3, 0.5.
  • scoring.needs_review.dead_band: Band around each threshold that is still flagged. Default: 0.1.

Configure the BoltDB-backed resource history store that tracks cloud resource identity across replacements and deletions. The history store enables accurate full-month cost reporting by remembering all cloud IDs a resource has ever had.

Important: Unlike the cache, the history database contains persistent data that cannot be regenerated. Deleting history.db causes accuracy degradation for past billing periods.

Option Type Default Env Var Description
cost.history.enabled bool true FINFOCUS_HISTORY_ENABLED Master switch for resource history tracking. When disabled, all history operations are no-ops and commands fall back to current-state-only behavior.
cost.history.retention_days int 90 FINFOCUS_HISTORY_RETENTION_DAYS Days before stale entries are removed. Entries with LastSeen older than this window are cleaned up on startup.
cost.history.directory string <FinFocus home>/history FINFOCUS_HISTORY_DIR History database directory.
cost.history.pulumi_cloud.enabled bool or string enum false None Import Pulumi Cloud resource identities: false disables requests, auto falls back to local history, true requires a complete import. Requires cost.history.enabled.

The history directory resolves in this order: FINFOCUS_HISTORY_DIR, then cost.history.directory, then <FinFocus home>/history. FinFocus home is FINFOCUS_HOME, then $PULUMI_HOME/finfocus, then ~/.finfocus (or ./.finfocus when no home directory can be found). When FINFOCUS_HOME and PULUMI_HOME are unset, the directory is ~/.finfocus/history.

FinFocus ignores a project-level cost.history.directory and logs a warning when it is absolute, contains .., or resolves outside the project (for example through a symlink), or when a history database file in it is a symlink, because a cloned repository controls that file. The global config value and FINFOCUS_HISTORY_DIR are trusted.

cost.history.pulumi_cloud.enabled accepts YAML/JSON booleans false and true, or strings "false", "auto", and "true". Other values are rejected. Both global and project configuration may set it. config get and the Config page display string values; config set and the Config page save strings.

Terminal window
finfocus config set cost.history.pulumi_cloud.enabled auto
finfocus config get cost.history.pulumi_cloud.enabled

The dashboard Config page provides an editable enum row for cost.history.pulumi_cloud.enabled with the three choices and default false. Overview and web cost views use stored history; they never import from Cloud. See the Resource History Guide for import behavior and observation limits.

Pulumi Cloud credentials come from the Pulumi CLI login or PULUMI_ACCESS_TOKEN; no token config key exists. An environment token override for a project’s backend.url requires PULUMI_BACKEND_URL to explicitly select that backend. Without it, only the token saved for that URL is used.

cost:
history:
enabled: true
retention_days: 90
directory: "" # FINFOCUS_HISTORY_DIR, else <FinFocus home>/history
pulumi_cloud:
enabled: auto # false (default), auto, or true

Configure tag-based cost attribution for resources that lack direct billing identity. This is a Layer 2 feature — the configuration is validated and stored, but the tag-based query logic is deferred to a future milestone.

Option Type Default Description
cost.allocation.enabled bool false Will enable tag-based cost attribution fallback. The configuration is validated and stored now; tag-based query logic will be applied in a future milestone.
cost.allocation.tags string list [] Tag keys that will be used for billing API queries (e.g., pulumi:project, aws:createdBy). Each key must be non-empty and at most 128 characters. Validated on load but not yet applied for attribution.
cost:
allocation:
enabled: true
tags:
- "pulumi:project"
- "aws:createdBy"
- "team"

Configure budget limits, alerts, and cost calculation preferences.

Boolean, default false. When true, Terraform history identity includes the workspace name from terraform workspace show (or tofu). When false, FinFocus does not ask for the workspace and uses the stack name unscoped. The setting does not change a Pulumi history key.

Default workload hint for projected cost and recommendations. Accepted values are dev, development, prod, production, burst, and default. An omitted field means no preference. An empty value is a configuration error. default is the same as no preference. --profile and FINFOCUS_PROFILE override this field for one run and do not rewrite the file. cost actual and cost history collect accept the flag so a bad value fails early, and they do not send the hint.

The cost.budgets section supports hierarchical scoping with global, providers, tags, and types sections.

Global budget applied to all resources.

Option Type Default Description
amount number - Required. The budget limit amount.
currency string USD ISO 4217 currency code.
period string monthly Budget period (daily, weekly, monthly, yearly).
alerts list [] List of alert definitions.
exit_on_threshold boolean false Whether to exit CI/CD when the budget threshold is reached (global and per-scope).
exit_code number 2 Exit code when budget exceeded (CI/CD integration).

Per-provider budgets for multi-cloud cost control.

Option Type Default Description
<provider> object - Provider name (aws, gcp, azure) with budget settings.
<provider>.amount number - Required. Provider budget limit.
<provider>.currency string Global currency Must match global budget currency.

Tag-based budgets for team/project cost allocation.

Option Type Default Description
selector string - Required. Tag pattern (key:value or key:*).
priority number 0 Priority for overlapping tags (higher wins).
amount number - Required. Tag budget limit.
currency string Global currency Must match global budget currency.

Per-resource-type budgets for category control.

Option Type Default Description
<type> object - Resource type (e.g., aws:ec2/instance) with budget settings.
<type>.amount number - Required. Type budget limit.
<type>.currency string Global currency Must match global budget currency.
Option Type Default Description
threshold number - Required. Percentage of budget (1-100) to trigger alert.
type string actual Trigger on actual (historical) or forecasted (projected) cost.
notifications list [] Destinations notified when the threshold is exceeded (see below).

cost.budgets.<scope>.alerts[].notifications

Section titled “cost.budgets.<scope>.alerts[].notifications”

Each destination is sent only in runs with --notify or FINFOCUS_NOTIFY=true.

Option Type Default Description
type string - Required. slack (incoming webhook) or webhook (generic HTTPS endpoint).
url string - Required. https:// URL with a host, or ${FINFOCUS_NOTIFY_*} references.
channel string - Slack only. Channel override sent with the message.
method string POST Webhook only. POST or PUT.
headers object - Webhook only. Extra request headers; values may use ${FINFOCUS_NOTIFY_*}.

Only variables named FINFOCUS_NOTIFY_* expand, and only in the global config. A project config destination must use literal values; validation rejects any ${...} there. config get and config list show url and header values as [REDACTED] unless the value is a single ${NAME} reference. See Budget Notifications.

cost:
budgets:
global:
amount: 10000.00
currency: USD
period: monthly
exit_code: 2
alerts:
- threshold: 80
type: actual
providers:
aws:
amount: 5000.00
gcp:
amount: 3000.00
tags:
- selector: 'team:platform'
priority: 100
amount: 3000.00
- selector: 'env:prod'
priority: 50
amount: 5000.00
types:
'aws:ec2/instance':
amount: 2000.00
'aws:rds/instance':
amount: 3000.00

See Budget Configuration Guide for detailed usage.

For IDE autocompletion (VS Code, JetBrains), add this comment to the top of config.hujson:

// yaml-language-server: $schema=https://rshade.github.io/finfocus/schemas/config.json

A legacy config.yaml can keep the same schema comment with a leading #.