-
Notifications
You must be signed in to change notification settings - Fork 0
secrets
harnessed works fully without any secrets backend. This guide is for operators who
want 1Password-backed secrets resolved into their stacks at launch — opt-in, env-only,
never baked into an image or committed file. For the why (threat model, design rationale),
see docs/harnessed-design.md §16.
With a .env.schema present, secrets resolve from 1Password via varlock
and reach the pod members as environment variables only — never written to a profile, a
container image layer, or a repo file. Absent the schema, harnessed is bit-for-bit today's
behavior: varlock is never invoked, no op call, no env mutation. The opt-in switch is a
single filesystem test.
# 1. Copy the shipped template into your XDG harnessed config dir.
mkdir -p ~/.config/harnessed
cp .env.schema.example ~/.config/harnessed/.env.schema
# 2. Edit the op:// refs to match your 1Password vault items.
# Example (Private vault, items named "Snyk" and "SocketDev", field "credential"):
# SNYK_TOKEN=op(op://Private/Snyk/credential)
# SOCKET_SECURITY_API_KEY=op(op://Private/SocketDev/credential)
$EDITOR ~/.config/harnessed/.env.schema
# 3. Launch any stack. Resolved secrets reach the pod as env only.
harnessed claude_timeThe .env.schema.example shipped at the repo root is the canonical template — copy it,
don't author from scratch. The @plugin(@varlock/1password-plugin@1.2.0) +
@initOp(allowAppAuth=true) decorators are required (they wire the op(op://…) resolver
and tell varlock to use the mounted agent socket for app-auth).
harnessed runs varlock on the host to resolve op:// refs. This is required, not a
preference: 1Password's desktop app authorizes the op CLI by calling application (your
terminal), so app-auth (@initOp(allowAppAuth=true)) works on the host but cannot work
from inside a container — there the desktop app has no host app to bind the grant to, and op
fails with "cannot connect to 1Password app" no matter which socket is mounted. (The
~/.1password/agent.sock mounted into every stack is the SSH agent, for git signing — not
the op app-auth transport.)
- The launcher detects
~/.config/harnessed/.env.schema(one[ -f ]test — inert when absent; with no schema,varlockis never invoked). - It runs
varlock load --format envon the host, in the schema's directory. The first run prompts the 1Password desktop app to Authorize your terminal for CLI access — approve it once and the grant persists. - The resolved dotenv is captured into a mode-0600 temp
--env-fileunder$TMPDIR. - That
--env-fileis spread into the launched container(s) — both pod members, the sidecar (svc up), and the scan step (harnessed build) — so resolved secrets reach the container as env only, never a profile, image layer, or repo file (T-05-05). - The temp file is unlinked after launch (T-05-06).
This needs varlock on the host (npm i -g varlock); op (already on most 1Password hosts) is
driven by varlock via app-auth. The "podman-only host" invariant still holds for the
no-secrets path — varlock is never touched without a schema. Hosts without host varlock
fall back to the headless path below.
For environments without the 1Password desktop app or without host varlock (CI, the
nightly re-scan timer, a headless server), set OP_SERVICE_ACCOUNT_TOKEN in the launcher env.
With a service-account token, resolution runs host-native in-process (HTTPS bearer auth — no
desktop app, no app-auth, no socket). harnessed forwards the token only when it is already set
— it never prompts and never echoes.
Caution (per CLAUDE.md "What NOT to Use"): a visible service-account token leaks into any process sharing the env. Scope it narrowly to the invocation — prefix it on the command line (
OP_SERVICE_ACCOUNT_TOKEN=… harnessed claude_time) or inject via your CI secret store. Do notexportit in your shell profile or~/.bashrc, and do not leave it in a long-lived shell session.
Sidecar services (hindsight, openbrain, …) can declare their own schemas at
~/.config/<service>/.env.schema — e.g. ~/.config/hindsight/.env.schema for the hindsight
sidecar. The schema syntax is identical; see the .env.schema.example header comment.
The in-image supply-chain scan uses snyk when a token is available, and warn-skips otherwise (osv-scanner + pip-audit always run). The token reaches the scan as a build secret, never a build-arg:
- Declare
SNYK_TOKENin~/.config/harnessed/.env.schema(e.g.SNYK_TOKEN=op(op://Private/Snyk/credential)) and havevarlockinstalled._build_derived_imagethen wraps the derivedpodman buildinvarlock run … -- podman build … --secret id=snyk_token,env=SNYK_TOKEN; the scan layer reads it from/run/secrets/snyk_token, so it never enters image history. - No schema entry / no varlock → a plain build with no secret, and snyk warn-skips
(
snyk skipped (no SNYK_TOKEN build secret)) — correct non-interactive behavior.
Get a Snyk token at https://app.snyk.io/account/personal-access-tokens (Account settings → Personal Access Tokens → Generate).
After launching with a schema present, confirm secrets reached the pod and did not leak:
# 1. The value reached the pod env (replace <instance> + <KEY> with your own):
podman exec <instance> env | grep SNYK_TOKEN
# expected: SNYK_TOKEN=<the resolved value>
# 2. The value is NOT in the committed profile:
grep -r SNYK_TOKEN profiles/ # expected: no matches
# 3. The value is NOT baked into the image:
podman history harnessed-base:latest | grep -i snyk # expected: no matches
# 4. The temp env-file is gone after launch:
ls /tmp/harnessed-env.* 2>/dev/null # expected: no matches (unlinked post-launch)If podman exec … env | grep <KEY> returns nothing but you expected it to, check that:
-
~/.config/harnessed/.env.schemaexists and has a non-@optionalentry for<KEY>. - The 1Password desktop app is running and unlocked (the agent socket must be live for
app-auth), OR
OP_SERVICE_ACCOUNT_TOKENis set in the launcher env. - The
op(op://Vault/Item/field)ref points at a real vault item.
- docs/harnessed-design.md §16 — the design rationale (the why).
- docs/harnessed-design.md §7 — supply-chain scanners + the warn-and-skip contract.
- .env.schema.example — the canonical schema template.
Start Here
Guides
- Recipe authoring
- Service authoring
- Stacks
- Extending stacks (proposed)
- Recipe catalog
- System prompt & rules (proposed)
- Secrets
- AWS SSO
- Pulumi (host login forwarding)
- Egress & exposing services
- Container filesystem
- Git hooks
- Troubleshooting
- Pin management (harnessed update)
Codebase Map
Planning & Roadmap
- open work: GitHub Issues
Research & Prompts
- research/ (home-folder requirements per harness, browse in-repo)
- prompts/ (reusable prompt templates, browse in-repo)