Skip to content

Secrets and credentials ​

This page covers the difference between the secrets that start the installation and the credentials you connect inside it.

Before you begin ​

  • Know how your installation runs, because that sets where the secrets file is.
  • openssl on your PATH, to make a key.
  • For provider credentials, the Tower open at Integrations.

Four bootstrap secrets stay in the environment ​

NoticeOS needs four secrets before it can read its own store. They cannot be stored in the product, because the product is not running yet.

SecretWhat it does
CREDENTIALS_KEYThe key every stored provider credential is encrypted under. Make one with openssl rand -base64 32.
OPERATOR_TOKENThe token the runner and your own terminal commands present to the installation's write routes.
ASSET_TOKENSOne shared token per site, checked when a site sends its report and presented when the installation pulls one.
DATABASE_URLThe address of the installation's own Postgres database, as the application login.

They are stored in one file, workers/ingest/.dev.secrets.json, which is never committed. It is in the installation folder's state/ (/state/workers/ingest/.dev.secrets.json in the app container), and pnpm os:setup generates it.

Edit it as formatted JSON. After a change, run pnpm os:restart so it is read again. The app compiles the file to a generated .dev.vars beside it at every start. DATABASE_URL never reaches the app as a binding; the runner reads it at start and passes it to the server's environment only.

Never put a provider credential here first

The environment is for the four secrets in the table. A Google key, a DataForSEO login or a webhook URL belongs on the Integrations page, not in this file.

What never leaks ​

Secrets are removed from logs before they are written to disk and again when pnpm os:logs reads them. Status output never shows an address, a token or a raw database error. Test fixtures use invented values.

Connect a provider credential ​

Every provider credential is connected in the product.

  1. Open Integrations (/integrations).
  2. Select the provider and Connect. The panel has a field for each value that provider needs and shows where to find it.
  3. Select Test connection. It tests the credential with one real call and shows a live sample.
  4. To remove it later, select Disconnect.

Each credential is stored AES-GCM encrypted in the database under CREDENTIALS_KEY. Saving a new value inserts the next version and removes the previous one in a single transaction. Every collector reads the store first.

The providers available today include Google Analytics and Search Console, Bing Webmaster, DataForSEO, Microsoft Clarity, PostHog, Mediavine, Discord, calendar feeds and Cloudflare.

Move credentials out of the environment ​

Older installations kept provider credentials in the secrets file. That still works: a provider with no stored credential falls back to its environment value, and its card on Integrations shows a Legacy env chip. System health shows in one line how many connections still use a legacy credential.

  1. On Integrations, on any Legacy env card, select Import from this machine. The whole secrets file is imported.

There is no import command; use the button. The move is optional. Not moving costs portability: a fresh installation would need those values copied by hand.

Check every credential ​

  1. In the checkout, run the check:

    sh
    pnpm creds:check                        # every configured credential, one real probe each
    pnpm creds:check --lane dataforseo      # one provider only
    pnpm creds:check --origin http://127.0.0.1:6000   # a Tower on another port

Each row shows whether the credential came from the store or the environment, and shows a live data sample or the provider's actual error and the likely fix. It never prints a secret value.

Two providers are probed only when you name them explicitly, as in the second example: a Clarity probe spends one of ten daily calls, and a Discord probe posts a real message.

Rotate the encryption key ​

Rotation re-encrypts every stored credential under a new key, one connection at a time, with every credential readable throughout. It runs on the machine that hosts the installation.

  1. In a terminal, generate the new key:

    sh
    openssl rand -base64 32
  2. In workers/ingest/.dev.secrets.json, set CREDENTIALS_KEY_PREVIOUS to the current CREDENTIALS_KEY, and CREDENTIALS_KEY to the new value.

  3. Restart so both keys are loaded: pnpm os:restart.

  4. Run the sweep:

    sh
    pnpm creds:rotate-key
  5. Remove CREDENTIALS_KEY_PREVIOUS from the file and restart once more.

While both keys are present, every read tries the current key and then the previous one, so a collector that fires mid-sweep still works.

Verify ​

  • pnpm creds:check shows every row with its source (store or environment) and a live data sample.
  • No card on Integrations shows a Legacy env chip, and System health no longer counts legacy credentials.
  • pnpm creds:rotate-key prints counts and provider names, nothing else, and exits zero.

If it didn't work ​

  • creds:check prints that it is running the env-only check: the installation is stopped, so store-held credentials cannot be tested. Start it, or pass --origin with the right port.
  • A card still shows Legacy env: the credential is still stored in the environment file. Select Import from this machine.
  • creds:rotate-key exits non-zero: a row could not be re-encrypted. Keep CREDENTIALS_KEY_PREVIOUS in place and run the sweep again before removing it. See Troubleshooting.

The line between bootstrap secrets and integration credentials is written once, in doc 06, Operations & self-observability. The credential commands are at https://github.com/notice-cx/NoticeOS/blob/main/scripts/README.md#credentials-credscheck.

Next steps ​

Released under the AGPL-3.0 license.