~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

Owners give their runners secrets with knotless secret

Merged
Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:run/owner-secrets

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 with dev.knotless.putSecret. ls lists them, one line per runner, never a value; rm takes one back from every runner. It fails if the repository names no runners, or a runner publishes no key.

The MCP secret tool 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 --secrets file 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 whose refs reach 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 check error, as an unreadable file does.

Runners coming and going. knotless runners <url> remove deletes the secrets sealed to that runner. add warns 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.md and 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, deleteSecret and listSecrets, 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::secrets calls them, and knotless's login asks for them in its rpc: scope. docs/reference/api.md, docs/design/access.md and docs/design/security.md say 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.rs secrets_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.rs owners_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.rs a_merged_pull_passes_its_push: the owner seals TOKEN to 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

  • A runner that is also the repository's owner is listed every secret without its value, as owners are, so it can't open them: it uses its secrets file instead.
  • Organizations: only the account that owns the repository can set its secrets, so an org admin can't yet.
  • Base mode HPKE doesn't authenticate who sealed a value, so the appview could swap one in, though it could never read one.

🤖 Generated with Claude Code

Merged as 9e0ea5a
Merging this pull closed these issues