agentleFS
bks-labbks-lab/open-bridge/skills/secrets/SKILL.md

secrets

Skill13 starsChanged 13 days agoMITView on GitHub

Resolves a secret reference to the program that needs it, never the conversation, per rules/secret-placement.md and the infra/secret-stores/ policy. Commands: refs, check, run, where, store, stores, audit. No command prints a secret. Trigger: "/secrets", "secret", "token", "credential", "keychain".

13 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use ship.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • AWSSkill

    ship

    Finalize dev work on an issue branch — update docs, push, and open a PR.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

5 rules. 0 are common, 2 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 10 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read the referenced file ONLY when triggered.

    line 23
  2. Very specific:

    There is deliberately no command that prints a value: Not behind a flag, not behind a confirmation, not in --json.

    line 40
  3. Very specific:

    Read a plan as a plan. Two things belong to this design and are NOT here, and the first of them is a verb, so typing it gets an argparse …

    line 126
  4. Very specific:

    Never print a value: Not in an answer, not in a file, not in a commit, not in a test fixture.

    line 194
  5. Distinctive:

    A write is proved by a read-back, never by an exit code: Every tool involved exits 0 for an entry that holds nothing, and a value that lost …

    line 202

“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Secrets

Owns every secret reference written down in this tree: what they are, whether they still resolve here, and how a value reaches a program without reaching the answer. Read the referenced file ONLY when triggered.

The engine is skills/secrets/engine/ behind the shim skills/secrets/secrets.sh. The grammar it parses is the machine readable twin of rules/secret-placement.md, which stays the source of truth for which schemes exist and where a secret belongs.

The principle

An agent reads this output. Whatever is printed here sits in the model's context for the rest of the session, in the transcript, and in whatever log the harness keeps. So this skill hands out three things about a secret and never a fourth: its NAME (the reference), its LENGTH in bytes, and a FINGERPRINT (the first eight hex characters of its sha256). That is enough to tell two live tokens apart, to see that a rotation actually landed, and to prove that something was read. It is not enough to use.

There is deliberately no command that prints a value. Not behind a flag, not behind a confirmation, not in --json. run is the one path a value takes, and it goes into a child process rather than into the answer: the child's own output is scrubbed on the way back, and a stream that still holds the value after scrubbing is dropped rather than printed.

The second thing the report carries is the one nobody had: CONTEXT. The same reference is readable from a desktop session and refused over ssh. A report that says ok on a laptop and missing on the same laptop over ssh is not measuring the vault, it is measuring the session, and it has to say which.

Where a new secret goes

The other half of the question, and the half that had no answer. An agent handed a token decided for itself where to keep it, and the result was small text files with credentials in working folders on two machines. The answer is declared once, in infra/secret-stores/*.yaml: one file per store, saying where that store is, who reaches it from where, and which KIND of secret belongs in it.

The kinds are a closed list, because the question is a policy question and not a guess about the value: personal-token, org-credential, customer-credential, service-runtime, ci-secret, household-shared, break-glass. where prints what each one means and which store holds it.

PII is explicitly not a kind. An IBAN, a tax id and a postal address identify a person, they do not authenticate one. Moving them into a vault makes them useless for what they are for and buys nothing, so where iban refuses with that sentence rather than proposing a store. Personal data belongs where it is used; audit will report it where it lies.

Arguments

Seven verbs. secrets.sh resolves its own real path through the discovery symlink, so it can be called from anywhere.

ArgumentEffectDefault
refs [path...]Every reference in the tree, grouped, with file and linewhole tree from --root
check [ref...]Resolve each reference: status, bytes, sha256, whereevery reference in the tree
check --allMeasure the whole tree even when references are namedoff
check -vAlso print every file and line a reference is written atoff
run --env NAME=REF -- cmdResolve REF into the child's environment as NAMErepeatable, none
run --stdin REF -- cmdResolve REF and write it to the child's standard inputnone
run --if-missing error|warn|ignoreWhat an unresolvable reference doeserror
where [kind]Which store a new secret of this kind belongs in, and what to call itno kind: list the kinds
where --owner NAMENarrow to the persona, org or customer it is forevery owner
store REFWrite a value into the store that answers REF, then read it backvalue from stdin when piped
store --from stdin|clipboard|promptWhere the value comes from. Never argvauto: stdin when piped, else a hidden prompt
store --kind KINDRefuse the write unless the target store declares this kindinferred when the store holds exactly one
store --replaceOverwrite an entry that is already thereoff
store --tag KEY=VALUEMetadata written in the same call, where the backend carries itrepeatable, none
storesEvery declaration, what it holds, and what is wrong with itwhole tree from --root
auditPlaintext that should have been a reference, with where each value belongswhole tree from --root
audit --also PATHAlso scan a directory outside the tree, where the loose files actually arerepeatable, none
audit --no-piiCredentials only. Personal data is reported by default and never movedoff
audit --with-gitleaksAsk gitleaks for a second opinion, when it is installed hereoff
audit -vAlso show the hits inside declared stores, and why each pattern existsoff
--root PATHTree to read declarations from.
--keychain PATHAddress this keychain file instead of the search list. Spell it absolutely: security reads a relative path as the login keychain on a writethe search list
--db NAME=PATHWhere a KeePass database lives on this machinenone declared
--db-password-ref REFReference holding the master password of those databasesnone
--key-file PATHKey file for the KeePass databasenone
--jsonMachine readable output, same fields, still no valuesoff

What an audit proves, and what it does not

It proves the positive and nothing else. A value that matches one of the shapes, in a file that was read, is reported with its file, its line, eight characters of the match and the store it belongs in. That much is evidence, and it is the half worth acting on today.

A clean scan is not a proof, and the last line of every report says so. In a git tree the scan reads the TRACKED set, so the scratch file nobody added is invisible until --also names its directory; it reads the working tree and not the history, so a value deleted this morning is still in the log; a shape no pattern knows walks past, and so does a value that is split over two lines or wrapped in something. --with-gitleaks buys a second opinion with several hundred more shapes and still does not turn any of that into an absence.

Two findings are deliberately not problems. A hit inside a directory that a file store declares is the store working, shown only under -v. Personal data is reported where it lies and never proposed for a vault, and it does not change the exit code: 0 when no credential is loose, 3 when one is.

What is not here yet

Read a plan as a plan. Two things belong to this design and are NOT here, and the first of them is a verb, so typing it gets an argparse usage error and exit 2:

VerbWould doState
rotatemint the next value at the provider, store it, prove the read-back, then retire the old onenext slice
--owner on a writecheck the value against the owner line of the policy, the way --kind is already checkedwhere --owner narrows; store cannot yet

The same applies one layer down, and it is now the read and the write that differ rather than the scheme list. The grammar parses six schemes and five backends read: keychain://, keepass://, azure-keyvault://, 1password:// (op://) and file://. vault:// parses cleanly and then reports that this Bridge cannot reach it yet, naming what it does reach. That is a missing backend, not a broken URI, and it is worth saying out loud before somebody edits a correct reference to fix it.

Four of those five write, and the fifth refuses on purpose. 1password:// is read only here, because op item create and op item edit take the value as a command line argument. Making the item in the app and pointing a reference at it keeps the value out of the process list; doing it through op would not.

Decision Tree

User wants to...
├── See every secret reference in the tree   → run `secrets refs`
├── "Is that token still there / still good" → run `secrets check`, then
│                                               references/resolve.md (§ Worked example: check)
├── Give a program a secret to run with      → Read references/resolve.md (§ Worked example: run)
├── Put a NEW value into a store             → Read references/store.md (§ The three sources),
│                                               then run `secrets where <kind>`
├── Know which store answers a reference,
│   or what a declaration has to say         → Read references/resolve.md (§ The declarations)
├── Understand a status or an exit code      → Read references/resolve.md (§ What a row means)
├── Reach a KeePass database for the first
│   time, or wire its master password        → Read references/resolve.md (§ The bootstrap)
├── Know why a read works locally and fails
│   over ssh                                 → Read references/resolve.md (§ Context)
├── Know where a NEW token belongs           → run `secrets where <kind>`, then
│                                               references/store.md (§ The placement policy)
├── Ask what the kinds are, or why PII is
│   not one of them                          → run `secrets where` with no kind
├── Look for values that never became a
│   reference                                → run `secrets audit`, then
│                                               references/audit.md (§ Worked example: a run)
├── Deal with something the audit found      → Read references/audit.md
│                                               (§ Worked example: a finding, fixed)
├── Ask why a pattern is there, add one, or
│   mark a fixture that has to look like a
│   token                                    → Read references/audit.md (§ One list instead of three)
└── Ask what the schemes are                 → Answer from rules/secret-placement.md

Reference map

FileOwns
references/resolve.mdThe READ side: how each scheme is addressed, what each backend calls, the store declarations a reference takes its options from, the measured macOS and KeePass facts, the bootstrap, and a worked example per verb
references/store.mdThe WRITE side: the three value sources, the read-back, what each backend does on a write, and the placement policy worked through
references/audit.mdThe SCAN side: the one pattern set and the three copies held to it, the pragma for a deliberate fixture, --also, the path from a finding to a store, and the limits of a clean scan
rules/secret-placement.mdWhere a secret belongs: the scheme table and the group hierarchy
infra/secret-stores/_template.yamlWhat a declaration says, field by field, and _schema.yaml next to it
engine/refs.pyThe grammar itself, and the only copy of it that runs
engine/patterns.pyWhat a plaintext secret looks like: one list, each entry carrying the marker the other copies are compared by. scripts/check-secret-patterns.py does the comparing

Hard Rules (non-negotiable)

  • Never print a value. Not in an answer, not in a file, not in a commit, not in a test fixture. Names, byte counts and fingerprints are the whole vocabulary. If a value is needed, it is needed by a program, and run is how it gets there.
  • Never pass a value in argv. Everything in argv is visible in ps to every process of the same user, which is how tokens ended up in the process list of two machines in this fleet. The tool that is called takes the value on stdin, and engine.exec.run has a stdin_bytes parameter for exactly that reason.
  • A write is proved by a read-back, never by an exit code. Every tool involved exits 0 for an entry that holds nothing, and a value that lost two characters to a quoting rule is indistinguishable from a correct one until something tries to use it. So store writes, reads the entry again, and compares length and fingerprint before it reports anything.
  • An empty entry is a miss, not a hit. security find-generic-password exits 0 for an item holding zero bytes. A check that tested existence reported green while the caller got an empty string and failed a layer later, where it looked like a permission problem. A row is green only when bytes came back.
  • A failed read over ssh is a session problem, not a missing secret. The login keychain has no unlocked session there. The entry may well be present, the password may well be right. A daemon that reads this as "gone" rotates a secret that was never lost.
  • Exit codes are the contract, because a wrapper that resolves its secret at startup has to tell a machine problem from a rotation nobody finished:

    CodeMeans
    0everything asked for resolved
    3missing: no entry, or an entry with no bytes in it
    5refused: the operation was understood and not done, because doing it is unsafe
    64the command line is wrong
    69not reachable here: the backend tool is absent, or this session cannot use it
    78bad reference: the URI does not parse, or no backend answers its scheme

    check collapses its rows into one of these, and a row saying not readable here or no backend here does not make it fail. The table of which row produces which code is in references/resolve.md (§ What a row means).

  • A reference is a locator, never a value. Parsing one is safe, printing one is safe, and committing one is the point of rules/secret-placement.md. Nothing in this skill writes a resolved value to disk.
  • Never resolve a secret on behalf of a unit file. A declaration carries the reference unchanged and the program resolves it in its own process at run time. A resolved value written into a unit file is the one place it must not be.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.