Skip to content

Secret management

How secrets are named, bound, resolved, and kept out of packs, corpus, state, and documentation.

Core principle

Secret names and provider bindings are configuration. Secret values are never stored in Conductor, corpus, packs, state, indexes, profile docs, or generated output.

A missing secret is an explicit failure — Conductor never falls back to a default, environment variable, or hard-coded value.

Architecture

Organization profile                  Secret provider (Bitwarden)
┌──────────────────────┐              ┌──────────────────────────┐
│ Secret names:        │              │ Secret values:           │
│   NOTION_API_KEY     │──bind──→     │   nt_api_xyz...          │
│   GITHUB_TOKEN       │──bind──→     │   ghp_abc...             │
│   BWS_ACCESS_TOKEN   │──bind──→     │   bws_def...             │
│   CONDUCTOR_AUTH     │──bind──→     │   conductor_token_...    │
└──────────────────────┘              └──────────────────────────┘
         │                                      │
         │  name                                │  value (ephemeral)
         ▼                                      ▼
    ┌─────────────────────────────────────────────────┐
    │              Secret resolution                   │
    │  name → provider query → ephemeral value         │
    │  Value never stored; delivered to caller only     │
    └─────────────────────────────────────────────────┘
         │
         │ ephemeral value (in-memory only)
         ▼
    ┌─────────────────────────────────────────────────┐
    │  Caller (agent, adapter, compiler)               │
    │  Uses value for one operation; never persists    │
    └─────────────────────────────────────────────────┘

Secret names

Secret names are declared in the organization profile and adapter configuration:

Secret name Purpose Required by
CONDUCTOR_MCP_AUTH_TOKEN Authenticate agent to Conductor MCP All requesting agents
CONDUCTOR_GATE_C_APPROVED Enable live Notion/GitHub reads Estate preparation tools
CONDUCTOR_READ_ALLOW_LIVE Permit live adapter reads Read adapters
CONDUCTOR_NOTION_READ_TOKEN Notion API access token Notion read adapter
CONDUCTOR_GITHUB_READ_TOKEN GitHub API access token GitHub read adapter
BWS_ACCESS_TOKEN Bitwarden Secrets Manager access Secret resolution component
BWS_ORG_ID Bitwarden organization ID Secret resolution component
BWS_PROJECT_ID Bitwarden project ID Secret resolution component
VPS_HOST Deploy host address Deploy operations
VPS_SSH_KEY Deploy SSH private key Deploy operations
TAILSCALE_AUTHKEY Tailscale authentication Deploy operations (self-hosted)
NOTION_WORKSPACE_ID Notion workspace identifier Notion adapter
NOTION_DATABASE_IDS Notion database identifiers Notion adapter
GITHUB_ORG_ALLOWLIST Permitted GitHub organizations GitHub adapter
GITHUB_REPO_ALLOWLIST Permitted GitHub repositories GitHub adapter

Provider bindings

The organization profile maps secret names to provider-specific lookup keys:

secrets:
  - name: CONDUCTOR_MCP_AUTH_TOKEN
    provider: bitwarden
    provider_key: conductor-mcp-auth-token
    required: true
  - name: CONDUCTOR_NOTION_READ_TOKEN
    provider: bitwarden
    provider_key: notion-read-token
    required: true

Rules: - Provider is always bitwarden (Bitwarden Secrets Manager) for Conductor - provider_key is the lookup key in Bitwarden, not the value - required: true means the secret must resolve or the operation fails - required: false means the secret is optional (operation proceeds without it)

Resolution path

1. Agent requests secret-dependent operation
2. Conductor identifies required secret names from config
3. Secret resolution component queries Bitwarden for each name
4. Bitwarden returns ephemeral value (in-memory, never logged)
5. Value is delivered to the requesting component
6. Component uses value for one operation
7. Value is discarded after use

Resolution is synchronous and per-request. Values are never cached across authorization boundaries. Each consult_plan or adapter read resolves secrets fresh.

Never-in-pack rule

Secret values must never appear in:

Surface Enforcement
Authorized packs Compiler strips secret references before emission
Corpus units Proposal validation rejects secret values
Notion state Adapter never writes secrets
GitHub repositories Pre-commit hooks and CI checks reject secrets
Generated documentation Generator excludes secret sections
Organization profile Only names and bindings, never values
Experience records Feedback validation strips secrets
Chat memory or logs Agent instruction: never echo secrets

Missing-secret behavior

Scenario Behavior
Required secret name has no binding Operation fails; error: secret.<name>.unbound
Provider unreachable Operation fails; error: provider.unavailable
Provider returns no value for key Operation fails; error: secret.<name>.missing
Optional secret missing Operation proceeds without it; warning emitted
Secret value appears in plan or evidence Authorization refused; finding: secret.leaked

Software-specific secrets

For Software deployments, per-repository secrets follow additional rules:

# corpus/lines/software/units/sw.std.secrets.md

Key points: - Canonical store: Bitwarden Secrets Manager (EU), project workspaces - Unlock with the Python secret-gate CLI (not legacy secret-gate.ps1); Windows requires pwsh 7.2+ - Use cached session when present - Prefer generic reusable key names (VPS_SSH_KEY, not my-app-vps-ssh-key) - Before creating a secret, bws secret list and reuse if suitable - New secrets: create placeholder → Hector updates value in vault UI

Per-repo CI secrets (Lab/Portfolio Free-plan orgs): - VPS_HOST — Tailscale IP of deploy host - VPS_SSH_KEY — Deploy SSH private key - TAILSCALE_AUTHKEY — Tailscale auth for Actions - Set after promotion via add-repo-secrets.ps1

Prohibited: - Hard-coding tokens in scripts or docs - Echoing secret values into chat/logs when avoidable - Committing secrets, API keys, or private keys to git

Adding a new secret binding

  1. Identify the secret's purpose and which component needs it
  2. Add the secret name to the organization profile's secrets list
  3. Create the secret value in Bitwarden with the matching provider key
  4. Update any adapter or component configuration to reference the name
  5. Never document the value — only the name and purpose
  • Privacy model: reference/security/privacy-model.md
  • Organization profile: about/organization-profile.md
  • Read access contract: evidence/read-access-contract.md
  • Connection guide (token retrieval): guides/connecting.md
  • Software secrets standard: corpus/lines/software/units/sw.std.secrets.md
  • Secrets standard: corpus/standards/secrets.yml
  • Authority model (secret rows in ownership table): reference/authority/authority-model.md