Secrets, Service Setup & Materializer
Trinity stores every API key, token, and config file your project needs in one encrypted store. The Secrets page in the sidebar is the full management surface — table view, filters, bulk import, per-target editing.
Where secrets land on disk
Most secrets are environment variables — they get injected into the agent's process at execution time and never touch your filesystem. A handful (like dexie-cloud.json or .env.local) need to live as files inside your project — Trinity writes those into your repo under a managed .gitignore block so they never get committed. See The materializer below.
The Secrets page
Open Secrets from the sidebar. You'll see a table grouped by key name. A key with two target overrides shows up as one row that expands to reveal the per-target values.
What each row tells you
| Column | Meaning |
|---|---|
| Key name | The env var name (e.g. STRIPE_SECRET_KEY) |
| Target | Which target this row binds to. "Project-wide" if it applies to every target. |
| Service | The service that owns this key (e.g. stripe, dexie-cloud) |
| Purpose | runtime (env var), provider (AI provider key), file (materializes to disk) |
| Value | Masked. Click the eye icon to reveal — Trinity decrypts on the server and shows briefly. |
| Updated | Last write time |
Filters
- Search — substring match on key name
- Targets — filter by one or more targets. "Project-wide" is its own toggle.
- Purpose — narrow to runtime, provider, or file-purpose secrets
Adding a secret
Click Add secret. The dialog asks for:
- Key name — the env var name (e.g.
STRIPE_SECRET_KEY) - Value — what you're storing. Sensitive by default; the input masks the value as you type.
- Targets — pick one or more targets, or leave as "Project-wide" for cascade fallback. One key can fan out to multiple targets in a single submit.
- Purpose — defaults to
runtime. Pickproviderfor AI provider keys,filefor config files (file purpose adds a destinations field — see The materializer). - Sensitive — UI dimension only; controls whether the value is masked by default. Every row is encrypted at rest regardless.
Trinity infers sensitive from the key name (*_SECRET, *_KEY, *_TOKEN, *_PASSWORD default to true) when you don't override.
Editing a secret
Click any row to open the edit dialog. You can change the value, retarget the binding (move from project-wide to target-specific or vice versa), update the label, or delete the entry.
The cascade resolution rule means: if you have a project-wide value for DATABASE_URL and a target-specific value for the same key on the api target, the API target gets its own value and every other target gets the project-wide one. No manual fan-out needed.
Bulk .env import
Click Import from .env to paste a .env file. Trinity:
- Parses the file (handles quoted values, escaped newlines, inline comments)
- Runs the sensitivity heuristic per key
- Lets you pick which targets each key binds to (or all → project-wide)
- Picks a conflict policy: keep existing, overwrite, or skip
- Submits everything in one transactional write — the whole batch rolls back on any failure
Per-row conflict overrides are available if you want different behavior for some keys. Result rows report created | updated | skipped | error so you can see exactly what happened.
Provider keys
The Project Settings → Provider Keys card is a focused view of purpose='provider' rows for AI providers (Anthropic, OpenAI, DeepSeek, Moonshot, Z.ai, Qwen, Ollama). Same encrypted store underneath; the card just narrows the table to provider rows.
You can also set provider keys globally (across all your projects) in App Settings → AI Models. Cascade rule: project-scoped wins over global.
The materializer
Some setup flows produce config files Trinity needs to write to disk — dexie-cloud.json, .env.local, certificates, or project-specific YAML. These get stored as purpose='file' secrets with destinations (where on disk to write them):
- target — inside the consuming app's directory (e.g.
apps/web/.env.localfor a turborepo target) - repo — at the repo root (e.g.
dexie-cloud.jsonnext topackage.json) - trunk — at your workspace root (project-wide config)
Trinity's materializer is what writes these files. It runs:
- Right after a worktree is created (so every parallel story sees the right files)
- After the setup runner finishes capturing values from a CLI
- After successful story commits (so freshly-written secrets land before the next phase reads them)
- On demand via Project Settings → Re-sync files
.gitignore management
Every materialized file gets added to a Trinity-managed block in your repo's .gitignore:
# === Trinity-managed (do not edit between markers) ===
.env.local
config/dexie-cloud.json
# === /Trinity-managed ===
Don't edit between the markers — Trinity will overwrite. Anywhere else in the file is yours to manage. If you delete a file-purpose secret, Trinity unlinks the file and prunes the line from the block on the next materializer pass.
How agents see your secrets
When a story executes, Trinity assembles the env var bag from:
- Target-specific rows for the story's target
- Project-wide rows (cascade fallback)
- Global rows (across all your projects)
The first hit wins per key name. Multi-target stories that don't have a clear target binding raise a gate so you can split the secret per-target or mark it project-wide.
For file-purpose secrets, agents just see the file on disk — the materializer wrote it before the agent ran.
Service setup gates
If you start a story that needs a service Trinity sees as unconfigured, the story pauses at a service setup required gate. The gate dialog has the setup runner pre-bound to the right target — finish the flow and the story resumes automatically. Agents can also raise this gate mid-execution via the signal_service_required tool when they hit a missing service that wasn't on Trinity's radar.
See Execution Gates for the full gate catalogue.