Skip to content

Use the browser dashboard

finfocus --web serves the overview, actual-cost, recommendations, and what-if estimate experiences as a local web page. It uses the same engine, plugin routing, calculations, filtering, sorting, formatting, and recommendation scoring as the CLI and terminal UI, so the browser shows the same numbers.

This guide covers the common tasks. For every flag, view, and status code, see the Web UI reference. For why the server is built the way it is, see Web UI design.

  • Install the plugins your resources need (finfocus plugin list shows what is installed). The browser adds no pricing of its own.
  • Have a Pulumi project, or exported Pulumi preview or state files. Terraform state is not supported by the browser dashboard.
  • Use a browser on the same machine that runs finfocus. The server listens only on 127.0.0.1 and has no option to listen on another interface.

Run FinFocus from the directory that contains your Pulumi program:

Terminal window
cd <your-pulumi-project>
finfocus --web

FinFocus detects the project, the current stack, and its state the same way finfocus overview does. It prints the session URL on stdout and opens your default browser on it. The page fills in progressively while plugins answer. The URL contains a session token, so keep it private.

To use a stack other than the current one, add --stack:

Terminal window
finfocus --web --stack dev

--project-dir still controls only configuration resolution. Source detection always starts from the working directory.

With one stack, the page names it in the header and in a single tab, so you can always see which stack’s costs are on screen. That is the stack --stack names, or the workspace’s selected stack (pulumi stack select) when you leave --stack out. A plan or state file and Terraform show no tab.

To compare stacks in one page, give --stack a list:

Terminal window
finfocus --web --stack dev,prod

You can also repeat the flag: --stack dev --stack prod. The page shows a tab for each stack, in the order you gave, and the header names the one you are looking at. Select a tab with the mouse, or focus the tab bar and use Left, Right, Home, and End. Each tab has its own Overview, Cost, Recommendations, and every other view, and nothing is added up across stacks.

A stack loads when you first open its tab, so a list of five stacks does not start five sets of plugins at once. Returning to a tab shows what it already loaded.

If a stack cannot load, its tab says failed and its view shows the same message a single stack would. The other tabs keep working. Restart the dashboard to retry a failed stack.

Every name must be a stack of the project in the working directory. An unknown name stops the command with exit code 2 and lists the stacks the project has. A list needs the project’s own stacks, so it cannot be combined with --pulumi-json or --pulumi-state.

On a machine with no graphical browser, or when you want to choose the browser yourself, suppress the automatic open:

Terminal window
finfocus --web --no-browser

Open the printed http://127.0.0.1:<port>/?token=<token> URL yourself. If FinFocus cannot start a browser, it logs a warning and keeps serving, so the printed URL still works.

The operating system picks a free port unless you set one:

Terminal window
finfocus --web --port 8484

A port that is already in use stops the launch with an error naming the port. Pick another port, or omit --port.

When you generate sources yourself, for example in a script, pass them explicitly:

Terminal window
pulumi stack export > state.json
pulumi preview --json > plan.json
finfocus --web --pulumi-state state.json --pulumi-json plan.json

An explicit source takes precedence over auto-detection. A preview file also loads by itself outside a Pulumi project:

Terminal window
finfocus --web --pulumi-json plan.json

Inside a detected project, a preview-only launch still exports the project’s current state for the overview. Pass --pulumi-state to supply that state yourself.

The companion flags work as they do on finfocus overview:

Terminal window
# Actual costs for September (YYYY-MM-DD or RFC3339)
finfocus --web --from 2026-09-01 --to 2026-09-30
# Query one installed plugin only
finfocus --web --adapter aws-public
# Only AWS resources
finfocus --web --filter provider=aws

These flags are valid only together with --web, and only on the root finfocus command. Sources and dates are fixed for the session and cannot be changed from the page. Restart the session to change them. The only file the page reads is the optional spend CSV on the Forecast view.

If the stack uses a passphrase secrets provider, the overview opens an Encrypted stack dialog. Enter the passphrase and choose Unlock. A wrong passphrase shows an inline error and lets you try again.

With several stacks, each encrypted stack asks for its own passphrase in its own tab, marked locked. Unlocking one stack does not unlock another. If you switch to a locked stack from another view, such as Cost, a notice above the view links to that stack’s Overview, where the prompt appears. A stack that failed to load shows the same kind of notice.

FinFocus hands the passphrase only to the pulumi subprocess environment. It never puts it in a response, an event, or a log line, and never saves it.

When the session starts from state only, choose Run preview in the overview to run pulumi preview on demand, like the p key in the terminal UI. The overview shows the elapsed time and updates pending changes when the preview finishes. Only one preview runs at a time. A resource’s detail panel lists its dependents under Blast radius; after the preview it includes the dependents the plan creates, changes or deletes. The terminal overview shows the same section.

The Blast radius table keeps its summary and column headings in view while a long list scrolls, and it fits a narrow window without scrolling sideways. The Status column spells out new, deleting, replacing, updating and component, so no state depends on color. Each dependent is a link: press Enter on it (reach it with Tab) to open that resource’s detail, then choose Back to the earlier resource, or Close detail to return to the row you started from.

There is no general refresh. To fetch fresh actual costs, stop the session and launch it again.

The What-If picker hides Pulumi provider and stack bookkeeping by default. It also hides resources confirmed non-billable by available, agreeing plugin pricing metadata. Show all resources restores the complete safe source list, within your launch filters. An eligible selected resource and its unsaved edits stay selected when you toggle it, provided the source properties are unchanged. If a preview changes those properties during the toggle, What-If reloads the editor and baseline from the updated source. A failed resource-list refresh shows a separate error and preserves the current estimate.

Zero baseline, zero rate, or zero actual spend does not establish non-billability. Unsupported resources, missing region or SKU, pricing errors, and unknown billability remain available for estimation. Metadata is used only when already available for the effective pricing providers; listing resources never triggers new pricing probes. A positive projected baseline helps choose the initial resource without affecting which resources are visible.

If only known non-billable resources remain, the picker explains the empty list and offers Show all resources. Planned resources and existing launch filters still apply. Interactive plan estimation uses the same default eligibility policy; explicit cost estimate --provider ... --resource-type ... remains available for inspecting a specific type.

Open Forecast to project monthly cost. The default is the plugins’ growth models, the same as finfocus cost forecast. To fit past spend instead, choose Statistical (ARIMA) and pick a CSV of date,value rows, one per consecutive day or month, exported from your billing console. The file stays on your machine; the page reads it and sends the text to the local server.

Add a Growth cap such as 1.25 to keep every month within 25% of the current one, or Steps such as 2027-06-01=500 for a known change. For a spike that will not repeat, add an Exclude range such as 2025-12-01:2025-12-31. The model order and confidence appear in the summary, and any warning the CLI would print appears under the table.

Open the printed URL again to start another tab or browser profile. The URL stays valid until the server stops. Reloading a page recovers the current load state instead of restarting the load.

If the page reports that a valid session cookie is required, the browser has no session for this server. Open the URL printed in the terminal.

Open Config in the header. The page lists each known setting, the value that is in effect, and whether that value came from the built-in default, the global file, the project file, or the environment.

Choose Project or Global, edit the values, and choose Review changes. The preview names each change and writes nothing. Confirm updates only that tier’s config.hujson. Cancel leaves the file as it was. Comments and keys the page does not edit stay in the file.

A secret is shown as set or not set. The field starts empty. Leave it blank to keep the stored value, or type a replacement. The page does not show the stored secret and does not write the masked placeholder back to the file.

The running dashboard keeps the configuration it loaded at startup. The new values apply to the next launch and to command-line runs. Alert destinations, routing, and analyzer settings are visible and are not editable on this page.

Open Plugins in the header. The page lists every built-in catalog plugin and any installed plugin that came from outside the catalog.

Choose Install, Update, or Remove. The confirmation names the plugin, the version, the source repository, and the security level. Nothing is downloaded until you confirm. Progress then shows each phase, including bytes received when the download reports a size. A second operation on the same plugin is refused until the first one finishes. Try it again afterward.

A plugin installed from outside the catalog can be validated, configured, and removed. Install and update stay on the catalog entries. The page does not accept a download URL. Custom registry addresses stay on the command line.

Validate checks the installed files and that the plugin starts. It does not certify the plugin.

Plugin settings use the same Project or Global choice as Config. Review the changes, then save. A secret field starts empty. Leave it blank to keep the stored secret. The page never shows the stored secret. Saved settings apply the next time that plugin is launched. The dashboard opens plugins again after a successful install, update, remove, or settings save. A plugin process that was already running keeps the environment it started with until that reopen.

An environment variable that is already set for the dashboard process takes precedence over the project file, the global file, and the plugin’s default. Set a secret that way when you do not want it written to a file.

When Pulumi ESC supplies a plugin setting, the Status cell reads from ESC (<reference>). The help text names that environment and says it overrides the shell and both configuration files. The value stays hidden.

The dashboard can show the log lines that FinFocus and its plugins write while the session runs. The view is off by default. Turn it on for one run with an environment variable:

Terminal window
FINFOCUS_WEB_LOGS=1 finfocus --web

Or turn it on for every run:

Terminal window
finfocus config set web.logs.enabled true

The environment variable wins over the setting, so FINFOCUS_WEB_LOGS=0 turns the view off for one run even when the setting is true. A value other than 1, true, 0, or false stops the launch with a validation error.

Open Logs in the header. Earlier lines from the initial load are already there, oldest first, and new lines follow. For debug lines, add --debug or set FINFOCUS_LOG_LEVEL=debug before you launch.

To narrow what you see:

  • Select source chips to show only core or specific plugins, or use the All sources / Core / Plugins select.
  • Choose a minimum level, such as warn+.
  • Type text in the filter box. It matches the message and field values, not field names. Turn on Regex to use a regular expression.
  • Press Pause to stop following the newest line while you read. The status line counts the lines that arrive meanwhile. Press Resume to follow again.
  • Press Clear to empty the view. This does not touch the log file.
  • Press Download NDJSON to save the rows currently shown, after filters, as a file with one JSON object per line.

For the same data in a terminal, run finfocus logs. It reads the log file once, with the same filters and the same redaction.

  • Credential-like field names and Pulumi secret values in fields are replaced with [redacted], in the page and in the downloaded file. The free-text message is shown as written, because it cannot be inspected reliably. Plugins should never log credentials in a message.
  • Log lines can contain resource names and file paths. The view is served only on 127.0.0.1 behind the session token, like the rest of the dashboard.
  • The server holds the newest 5000 entries in memory, and the page shows the newest 2000 rows. Older entries are dropped. The viewer writes nothing to disk, and the entries disappear when the session ends. The log file keeps receiving the same lines it does with the view off.

finfocus --web always listens on 127.0.0.1. To open it from another device on your tailnet, run a reverse proxy on the same machine, such as tailscale serve, and tell FinFocus the public name the proxy uses:

Terminal window
finfocus --web --no-browser --port 8765 --allowed-host dash.example.ts.net
tailscale serve --bg --https=443 http://127.0.0.1:8765

Open https://dash.example.ts.net/?token=<token> with the token from the printed URL. The session cookie is marked Secure because the proxy sends X-Forwarded-Proto: https.

  • --allowed-host is repeatable and matches the exact name. Wildcards, IP addresses, and values with a scheme or path are refused at startup. A port is optional; without one, the HTTPS default (443) is assumed. Any other Host is still refused with 403.
  • Each request must still carry the token cookie, so the proxy needs no special setup.

To skip the token, name the tailnet users who may open the dashboard:

Terminal window
finfocus --web --no-browser --port 8765 \
--allowed-host dash.example.ts.net \
--trusted-proxy --allowed-user alice@example.com

A request is admitted without the token only when it comes from the same machine, for an allowed host, with exactly one Tailscale-User-Login header that names an allowed user (case-insensitive). --trusted-proxy needs at least one --allowed-host and one --allowed-user, and --allowed-user needs --trusted-proxy. FinFocus prints a warning to stderr, then the token URL and the proxy URL on stdout.

Warning: Identity mode removes the token as a barrier against other users and processes on the same machine. Any local process can reach the loopback port and send the header itself. Use it only on a machine whose local users you trust.

  • Tailscale Serve sets Tailscale-User-Login for tailnet traffic only and strips client-supplied copies. Tailscale Funnel traffic and tagged devices carry no login, so use the token URL there.
  • Not yet verified live: whether Serve forwards the tailnet Host unchanged (if it rewrites it, list the rewritten name) and whether the live event stream survives Serve without buffering.

Closing the browser tab does not stop FinFocus. Press Ctrl+C in the terminal that launched it to shut down the server and its plugins.

Message Cause and fix
--port is only valid with --web (any companion flag) A companion flag was given without --web. Add --web. Exit code 2.
--web and --mcp are mutually exclusive Run the web dashboard and the MCP server as separate processes. Exit code 2.
unknown flag: --web --web was put on a subcommand, such as finfocus overview --web. Use finfocus --web.
no Pulumi project found in current or parent directories Run from the Pulumi project directory, or pass --pulumi-json.
port <n> on 127.0.0.1 is unavailable Another process holds the pinned port. Choose another --port or omit it.
valid session cookie required; open the URL printed in the terminal Open the printed URL, not the bare address.
invalid session token; open the URL printed in the terminal The token belongs to an earlier session. Use the URL from the current run.
No Logs link in the header Logs are off by default. Set FINFOCUS_WEB_LOGS=1 or web.logs.enabled to true and relaunch.
No cost data Check that the needed plugins are installed: finfocus plugin list.
  • Pulumi only. --terraform-state is not a --web flag.
  • A stack list needs a Pulumi project. There are no totals or comparisons across stacks; each tab is independent.
  • Loopback listener only. There is no option to listen on a non-loopback address and no built-in TLS. To reach the dashboard from other devices, put an HTTPS proxy on the same machine in front of it (Serve behind a proxy).
  • The browser cannot dismiss, snooze, or undismiss recommendations. Use finfocus cost recommendations dismiss and its siblings.
  • Run preview is offered even when the session already has preview data, and state-only sessions do not show the terminal UI’s Projected* marker (#1751).