chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
Owners give their runners secrets with knotless secret
MergedMerged as 9e0ea5a
Merging this pull closed these issues
- A repository owner can't give the hosted runner a secret kind/featurearea/cliarea/runner1
What changes
knotless secret.knotless secret <url> set <name> [--ref <pattern>]...reads a value from stdin (less one line ending), seals it to the key each runner the repository names publishes (dev.knotless.runnerKey), and leaves it at the appview withdev.knotless.putSecret.lslists them, one line per runner, never a value;rmtakes one back from every runner. It fails if the repository names no runners, or a runner publishes no key.The MCP
secrettool does the same, and its description says a value given there passes through the conversation.The runner reads them. For each name a workflow lists that the
--secretsfile does not grant, a push's run asks the appview for what was sealed to it for the repository, opens each with its keys, and gives those whoserefsreach the ref. The file wins a name both grant. Masking, no secrets for pulls and batches, and the not-granted line all hold. An appview that does not answer makes the checkerror, as an unreadable file does.Runners coming and going.
knotless runners <url> removedeletes the secrets sealed to that runner.addwarns which secrets must be set again to reach the new runner, since nothing keeps their values to seal again.Docs:
runner.md(Secrets),cli.md,mcp.md,reference/workflows.mdand the workflows guide.Why
A repository owner couldn't give the hosted runner a secret: only its operator could, by editing the runner's secrets file, so publishing to npm or deploying from the hosted runner meant handing the operator the token. This is the last part of the design on the issue.
Fixes A repository owner can't give the hosted runner a secret
It also carries what was pull 3 (The appview keeps secrets sealed to a runner, closed in its favour). The appview keeps secrets sealed to a runner: knotless-serve answers
dev.knotless.putSecret,deleteSecretandlistSecrets, each with a service token for itself. The owner lists names, runners and refs, never a value; a runner lists those sealed to it, with the sealed value; anyone else, none. Secrets are named by owner and repository key, so a rename keeps them.knotless::secretscalls them, and knotless's login asks for them in itsrpc:scope.docs/reference/api.md,docs/design/access.mdanddocs/design/security.mdsay so. The first two parts, the not-granted log line and the runner's key, have already landed.How it was checked
knotless-serve/tests/forge.rssecrets_reach_only_their_runner: the methods directly; put, replace, list as owner, runner and stranger, delete by name, runner and all, and each bad input refused with 400.knotless-serve/tests/forge.rsowners_seal_secrets_to_their_runners: set, ls and rm through the CLI; no runners, a runner with no key, and bad names, flags, refs and sizes refused; the runner opens the value without its line ending; a runner added later is warned of; a runner removed takes its copies; no appview fails.knotless-run/tests/run.rsa_merged_pull_passes_its_push: the owner sealsTOKENto main, and the push's run gets it, masked, while the pull's run says it gets none.knotless-mcp/tests/mcp.rs: set, list and remove through the tool.Everything here was type-checked locally; the tests are left to CI.
Shortcomings
🤖 Generated with Claude Code