# How provider credentials are stored

Anything secret is sealed at the moment it is saved, whichever screen it came in by, and it is never returned to a screen afterwards. What you see is a mask. Sending that mask back means *keep what is stored*, so a form cannot overwrite a working credential with the asterisks it was shown. Losing one means entering it again, not recovering it.

- **Status:** Available
- **Audience:** both
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/security/credential-sealing/

## Sealed at one point, on every door

Sealing happens where settings are written, not in the screen that happens to be collecting them. That is a correction of something worth stating plainly: it used to happen in one route, for one section, so other secrets — a carrier's token, a speech key — were stored as they arrived, whichever audience saved them. Moving the seal to the single point every save passes through closed that.

- Sealing is applied on the way in, so a value cannot be stored in the clear by arriving through a different screen.
- It is idempotent, and reading passes an already-clear value through unchanged, which is why the change repaired the older values on their next save rather than requiring a migration.
- It applies to the operator's settings and a customer's identically. There is no door where a secret is treated more casually.

## What a screen shows instead

A masked stand-in. Not a shortened version of the real value, not the last few characters, and never the value itself: once saved, a credential is not readable from any screen, any export or any interface.

| What the form sends | What is stored | Why |
|---|---|---|
| A new value | The new value, sealed | This is a genuine change |
| The mask it was shown | Nothing — the stored value is kept | The screen is echoing what it displayed; treating that as an edit would destroy a working credential |
| An empty field | Nothing — the stored value is kept | An untouched field is not an instruction to clear anything |

> **Note** This is why rotating a credential means pasting the new one in, and why there is no *reveal* button anywhere. A value you cannot read back is a value that cannot be read back by anyone else either — including anyone who reaches a screen they should not have.

## The database password lives in no file

The application's own database credential is not kept in configuration at all. The managed database service owns and rotates it into a secret store, and the configuration names that secret rather than holding a copy of the value. Boot reads it by name each time.

The reason is a specific failure. A copy in a configuration file goes stale the moment the real credential rotates — and nothing breaks at that instant. It breaks at the *next restart*, which may be days later, and it presents as the application suddenly being unable to authenticate at all. That reads exactly like a bad deployment, and it sends everybody to look at the wrong change.

> **Careful** Naming a secret and holding a secret are different things, and only one of them survives a rotation. The same reasoning applies to any credential a provider rotates on its own schedule: store the reference, not the copy.

## What is never done with a credential

**Returned to a screen** — No interface hands back a stored secret, in whole or in part.
**Included in an export** — What you export is your business data; credentials are not part of it.
**Written into a log** — Logs are read by people during incidents, which is the worst possible moment for a credential to be sitting in one.
**Used as an example** — Every example in this documentation uses an obviously fictional value, and the build refuses a page that looks like it carries a real key.
**Published here** — This page describes how credentials are handled. Where any of them lives is not part of a public manual.

The one authorisation that is visible to you is the mailbox connection, and even there what you see is a health verdict rather than a token — [Mailbox ownership](/docs/security/mailbox-ownership/) covers what a connection actually proves.

## Questions

### Can I see a credential I entered last month?

No. Sealed values are not returned to any screen, so there is nothing to reveal. If you no longer have it, get a fresh one from the provider and paste that in; replacing is the supported path and recovering is not.

### If I press save without touching the field, do I wipe the credential?

No. A form that returns the mask it was shown, or an empty field, is read as *keep what is stored*. Only a genuinely new value replaces the stored one, which is what stops an ordinary save from destroying a working connection.

### Why is the database password not simply in the configuration file?

Because the service that owns it rotates it, and a copy in a file goes stale silently. The failure appears at the next restart rather than at the rotation, so it looks like a deployment fault. Naming the secret and reading it at boot removes that whole class of surprise.

## Related

- [Security and isolation](https://connectbyjbrh.com/docs/security/)
- [Proving a mailbox belongs to a workspace](https://connectbyjbrh.com/docs/security/mailbox-ownership/)
- [Authorisation](https://connectbyjbrh.com/docs/security/authorization/)
- [Security settings](https://connectbyjbrh.com/docs/account/security-settings/)
- [Rotating a provider credential, end to end](https://connectbyjbrh.com/docs/workflows/credential-rotation/)
- [A copy of a password somebody else rotates](https://connectbyjbrh.com/research/stale-credentials/)

## What this page is based on

- `backend/app/settings_store.py` — sealing at the single write point and the mask rule
- docs-source/sources/GENERAL.md section 10 — sealed credentials and the managed database secret
- Connect capability registry (docs-source/facts.py)
- `docs-source/schema.py` — the build's own refusal of anything resembling a key
