From 9c1cc80a675ab8071822ce733bc3a4d46c225cb1 Mon Sep 17 00:00:00 2001 From: Taleodor Claude Date: Sun, 7 Jun 2026 14:29:00 +0000 Subject: [PATCH 1/2] fix(agent): drop --org from enrollkey; derive org from the key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The org is always the one the FREEFORM key resolves to, so requiring --org was redundant and a footgun — passing the key-order uuid (the segment after __ord__) instead of the org produced an opaque 'Not authorized'. Now the CLI sends no org and uses the new AgentSigningKeyInput. Simplifies runEnrollkey to the AGENT path (the unreachable committer branch is removed; operator committer enrol stays on the JWT mutation). Co-Authored-By: Claude Opus 4 (1M context) --- cmd/agentEnrollkey.go | 48 +++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/cmd/agentEnrollkey.go b/cmd/agentEnrollkey.go index cee8658..80b195d 100644 --- a/cmd/agentEnrollkey.go +++ b/cmd/agentEnrollkey.go @@ -27,12 +27,11 @@ import ( // not available (e.g. CI shipping a pre-computed value). var ( - enrollAgentUuid string - enrollKeyFormat string - enrollKeyOrg string - enrollPubkeyFile string - enrollFingerprint string - enrollKeyIdentity string + enrollAgentUuid string + enrollKeyFormat string + enrollPubkeyFile string + enrollFingerprint string + enrollKeyIdentity string ) var agentEnrollkeyCmd = &cobra.Command{ @@ -50,8 +49,8 @@ For SSH, --identity is required and must match the allowed_signers principal the verifier will check against (usually the user / agent email).`, Run: func(cmd *cobra.Command, args []string) { - if enrollAgentUuid == "" || enrollKeyOrg == "" || enrollKeyFormat == "" || enrollPubkeyFile == "" { - fmt.Fprintln(os.Stderr, "--agent, --org, --format, and --pubkey-file are required") + if enrollAgentUuid == "" || enrollKeyFormat == "" || enrollPubkeyFile == "" { + fmt.Fprintln(os.Stderr, "--agent, --format, and --pubkey-file are required") os.Exit(1) } pubKey, err := os.ReadFile(enrollPubkeyFile) @@ -59,12 +58,12 @@ allowed_signers principal the verifier will check against fmt.Fprintf(os.Stderr, "Failed to read pubkey file: %v\n", err) os.Exit(1) } - runEnrollkey("AGENT", enrollAgentUuid, enrollKeyOrg, enrollKeyFormat, + runEnrollkey(enrollAgentUuid, enrollKeyFormat, enrollPubkeyFile, string(pubKey), enrollFingerprint, enrollKeyIdentity) }, } -func runEnrollkey(ownerType, ownerUuid, org, format, pubkeyFile, pubKey, fingerprint, identity string) { +func runEnrollkey(ownerUuid, format, pubkeyFile, pubKey, fingerprint, identity string) { if fingerprint == "" { fp, err := deriveFingerprint(format, pubkeyFile) if err != nil { @@ -74,22 +73,16 @@ func runEnrollkey(ownerType, ownerUuid, org, format, pubkeyFile, pubKey, fingerp } fingerprint = fp } - // AGENT enrolment goes via the FREEFORM-auth'd programmatic - // mutation — that's what lets an agent bootstrap its own signing - // key without an operator JWT. COMMITTER enrolment stays on the - // JWT-authenticated path; only humans / CI bots admins enrol on - // committers. - // AGENT enrolment uses the FREEFORM-auth'd programmatic mutation; - // the wrapper argument is `signingKey`. COMMITTER enrolment stays - // on the JWT path which still uses the legacy `input` arg name. - op := "enrollSigningKey" - argName := "input" - if strings.EqualFold(ownerType, "AGENT") { - op = "enrollSigningKeyProgrammatic" - argName = "signingKey" - } + // AGENT enrolment goes via the FREEFORM-auth'd programmatic mutation + // — that's what lets an agent bootstrap its own signing key without + // an operator JWT. The org is not sent: the server always uses the + // org the calling key resolves to (AgentSigningKeyInput has no org + // field). COMMITTER enrolment stays on the JWT-authenticated + // `enrollSigningKey`, which is operator-only and not exposed here. + const op = "enrollSigningKeyProgrammatic" + const argName = "signingKey" query := ` - mutation ($` + argName + `: SigningKeyInput!) { + mutation ($` + argName + `: AgentSigningKeyInput!) { ` + op + `(` + argName + `: $` + argName + `) { uuid format @@ -102,9 +95,8 @@ func runEnrollkey(ownerType, ownerUuid, org, format, pubkeyFile, pubKey, fingerp } ` input := map[string]interface{}{ - "org": org, "format": strings.ToUpper(format), - "ownerType": ownerType, + "ownerType": "AGENT", "ownerUuid": ownerUuid, "fingerprint": fingerprint, "pubKey": strings.TrimSpace(pubKey), @@ -157,13 +149,11 @@ func deriveFingerprint(format, pubkeyFile string) (string, error) { func init() { agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollAgentUuid, "agent", "", "UUID of the agent — required") - agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollKeyOrg, "org", "", "Org UUID — required") agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollKeyFormat, "format", "", "Signature format: SSH or GPG — required") agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollPubkeyFile, "pubkey-file", "", "Path to the public key (single-line SSH or ASCII-armoured GPG) — required") agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollFingerprint, "fingerprint", "", "Override the auto-derived fingerprint") agentEnrollkeyCmd.PersistentFlags().StringVar(&enrollKeyIdentity, "identity", "", "Allowed-signers principal (required for SSH; e.g. email)") _ = agentEnrollkeyCmd.MarkPersistentFlagRequired("agent") - _ = agentEnrollkeyCmd.MarkPersistentFlagRequired("org") _ = agentEnrollkeyCmd.MarkPersistentFlagRequired("format") _ = agentEnrollkeyCmd.MarkPersistentFlagRequired("pubkey-file") From b19d977713f5b2b0304183ee4eaedcb8027f4314 Mon Sep 17 00:00:00 2001 From: Taleodor Claude Date: Sun, 7 Jun 2026 14:47:21 +0000 Subject: [PATCH 2/2] docs(agent): drop --org from enrollkey reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the flag removal — org is derived from the calling key. Co-Authored-By: Claude Opus 4 (1M context) --- docs/agentic.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/agentic.md b/docs/agentic.md index 10fe6ae..bab71f1 100644 --- a/docs/agentic.md +++ b/docs/agentic.md @@ -399,16 +399,16 @@ can bootstrap its **own** key on first run — no operator step needed. ```bash rearm agent enrollkey \ - --org "$ORG_UUID" \ --agent "$AGENT_UUID" \ --format SSH \ --pubkey-file ~/.ssh/agent_signing_key.pub \ --identity "agent@your-org.example" ``` -Required: `--agent`, `--org`, `--format` (`SSH` or `GPG`), -`--pubkey-file`. The fingerprint is derived locally from the pubkey -via `ssh-keygen` / `gpg`; pass `--fingerprint` to skip that local-tool -dependency. For SSH, `--identity` is required and must match the -allowed-signers principal the verifier checks (usually the agent -email). +Required: `--agent`, `--format` (`SSH` or `GPG`), `--pubkey-file`. The +org is **not** a flag — it is always the org the calling key resolves +to, so there is nothing to pass (and nothing to get wrong). The +fingerprint is derived locally from the pubkey via `ssh-keygen` / +`gpg`; pass `--fingerprint` to skip that local-tool dependency. For +SSH, `--identity` is required and must match the allowed-signers +principal the verifier checks (usually the agent email).