Skip to content

feat(cmd): forge auth subcommand for runtime-token operator UX (#162 part 1)#168

Merged
initializ-mk merged 1 commit into
mainfrom
feat/issue-162-auth-subcmd
Jun 15, 2026
Merged

feat(cmd): forge auth subcommand for runtime-token operator UX (#162 part 1)#168
initializ-mk merged 1 commit into
mainfrom
feat/issue-162-auth-subcmd

Conversation

@initializ-mk

Copy link
Copy Markdown
Contributor

Summary

First of a 3-PR stack closing #162. Adds three operator-facing primitives for the internal bearer token Forge mints at agent startup — the same token channel adapters use to call back into A2A (`runner.go:201-225`):

```
forge auth show-token Print the stored token to stdout.
Exits 1 with an actionable error pointing
at 'forge auth mint-token' when absent.

forge auth mint-token Generate a fresh 256-bit token, store it
at /.forge/runtime.token (0600),
print to stdout. For first-deploy bootstrap
from a clean checkout.

forge auth secret-yaml Print a ready-to-apply Kubernetes Secret
manifest with the token base64-encoded
into data.token. Default name and
namespace match what 'forge package' will
generate in part 3 of the issue.
```

The 3-PR stack

Part Scope PR
1 (this) `forge auth` subcommand — operator UX primitives, prerequisite for parts 2+3 this PR
2 `ScheduleBackend` interface + `FileBackend` (existing refactor) + `KubernetesBackend` (real client-go) + runtime auto-detection next
3 `forge package` generates CronJob / Secret-template / Role manifests last

This PR is independently shippable — operators get the token primitives today even without K8s scheduler.

Design choices

Secret-template parity with `forge package` — `forge package` (part 3) will emit a credential-less Secret template; `forge auth secret-yaml` is the one-liner that populates it. The two outputs are byte-identical except `forge package`'s template has no `data` field. Operator workflow:

```sh

In CI / from a clean checkout:

forge package # generates k8s/internal-token-secret.yaml (no data)
forge auth secret-yaml | kubectl apply -f - # populates the data field
kubectl apply -k ./k8s # applies everything else
```

`forge.agent.id` label always tracks forge.yaml, never `--name`. An operator using `--name` to clobber the Secret resource name still wants telemetry and label-selectors keyed on the real agent ID. Regression test (`TestAuthSecretYAMLOutput_LabelTracksForgeYAML`) pins this — caught during PR bring-up smoke.

```
$ echo "agent_id: aibuilderdemo" > forge.yaml
$ forge auth mint-token > /dev/null
$ forge auth secret-yaml --name custom-secret --namespace prod
apiVersion: v1
kind: Secret
metadata:
name: custom-secret # ← from --name
namespace: prod # ← from --namespace
labels:
forge.agent.id: aibuilderdemo # ← from forge.yaml, NOT --name
type: Opaque
data:
token: VUxEU0UwUGpETXRYMGlvSUpnbXZmSzF5YW5TbTBMbDM2X1c0RjhHWTlJbw==
```

Light-touch forge.yaml parse for `agent_id` rather than pulling the full `ForgeConfig` validation chain. The command runs outside the runtime, often in CI / bootstrap contexts where a validation error on an unrelated field would be a footgun. Falls back to `"forge-agent"` when the file is missing or `agent_id` is empty.

Files

File Change
`forge-cli/cmd/auth.go` (new) `forge auth` cobra command + three subcommands; `agentRootDir` helper resolving from `--output-dir`; `agentIDForSecretName` light-parse helper
`forge-cli/cmd/auth_test.go` (new) 6 tests covering parse cases, label-tracking invariant, token round-trip with 0600 perms
`forge-cli/cmd/root.go` Register `authCmd`

Test plan

  • `go test ./...` clean in forge-core and forge-cli
  • `golangci-lint run ./...` → 0 issues
  • `gofmt -w` applied
  • All 6 new tests pass
  • End-to-end smoke against `/tmp/forge-pr-auth`:
    • `auth show-token` from empty dir → exits 1 with clear error
    • `auth mint-token` → writes token, prints to stdout, exits 0
    • `auth show-token` after mint → prints same value
    • `auth secret-yaml` with default args → correct shape
    • `auth secret-yaml --name X --namespace Y` → name + namespace overridden, label unchanged

What's NOT in this PR (parts 2+3)

  • No K8s client-go dependency added. `forge auth secret-yaml` is hand-rolled YAML (one `fmt.Printf`) so this PR has zero new dependencies.
  • No scheduler backend interface. The existing 30s-ticker + `MemoryScheduleStore` is unchanged.
  • No `forge package` manifest changes. CronJob / Secret-template generation lands in part 3.

Refs #162

…part 1)

Three subcommands operating on the internal bearer token Forge mints
at agent startup (the same token channel adapters use to call back
into A2A — runner.go:201-225):

  forge auth show-token   Print the stored token to stdout.
                          Exits 1 with an actionable error pointing
                          at 'forge auth mint-token' when the token
                          file is absent.

  forge auth mint-token   Generate a fresh 256-bit token, store it
                          at <root>/.forge/runtime.token (0600),
                          print to stdout. For first-deploy bootstrap
                          from a clean checkout.

  forge auth secret-yaml  Print a ready-to-apply Kubernetes Secret
                          manifest with the token base64-encoded
                          into data.token. Default name and
                          namespace match what 'forge package' will
                          generate in part 3 of the issue.

This is part 1 of the 3-PR stack closing #162:

  part 1 (this PR) — operator UX primitives, prerequisite for parts 2+3
  part 2 — ScheduleBackend interface + FileBackend + KubernetesBackend
  part 3 — forge package generates CronJob/Secret/Role manifests

Design choices:

- 'forge package' (part 3) will emit a credential-less Secret
  template; this command is the one-liner that populates it. The
  two outputs are byte-identical except 'forge package's' template
  has no 'data' field.

- forge.agent.id label is always sourced from forge.yaml agent_id
  (or the "forge-agent" fallback), NEVER from --name. An operator
  using --name to clobber the Secret resource name still wants
  telemetry and label-selectors keyed on the real agent ID.
  Regression test (TestAuthSecretYAMLOutput_LabelTracksForgeYAML)
  pins this.

- Light-touch forge.yaml parse for agent_id rather than pulling
  the full ForgeConfig validation chain in — the command runs
  outside the runtime, often in CI / bootstrap contexts where a
  validation error on an unrelated field would be a footgun.

Tests cover:
- agent_id parse from unquoted / single-quoted / double-quoted yaml
- missing forge.yaml falls back to "forge-agent"
- the label-tracking-forge.yaml invariant (--name override doesnt leak)
- token round-trip writes 0600 at the expected path
- LoadToken contract for the show-token "no token yet" path
@initializ-mk initializ-mk merged commit 2691f46 into main Jun 15, 2026
9 checks passed
initializ-mk added a commit that referenced this pull request Jun 15, 2026
…knowledge skill

Sweeps the doc areas mapped to the code changes on this branch
(parts 2/2b/3 of #162) plus the recently merged work that landed
on main since the last sync-docs run.

docs/core-concepts/scheduling.md
  Replace single-paragraph "Execution Details" with a backend
  comparison table + the scheduler.yaml block + the cross-link to
  the new scheduler-kubernetes.md reference. Notes that K8s mode
  defers history to the audit stream instead of writing
  SCHEDULES.md.

docs/reference/forge-yaml-schema.md
  Add the `scheduler:` block under `schedules:` documenting the
  three backend modes and the `kubernetes:` tuning fields
  (namespace, service_url, allow_dynamic, trigger_image,
  auth_secret_name).

docs/reference/cli-reference.md
  Add the `forge auth` subcommand reference (PR #168 / #162 part 1)
  with the show-token / mint-token / secret-yaml flows and the
  label-tracks-forge.yaml invariant note.

README.md
  Link scheduler-kubernetes.md from the documentation index.

.claude/skills/forge.md
  Section 9 (Scheduling): hybrid Backend interface, FileBackend /
  KubernetesBackend table, InCluster() detection, AllowDynamic
  rule, cross-link to scheduler-kubernetes.md.
  Section 11 (Build pipeline): new ScheduleManifestStage row (6a)
  documenting the cronjob/secret-template/role triplet.
  Section 13 (CLI surface): `forge auth` row.
  Section 14 (forge.yaml): scheduler block in the example yaml.
  Section 18 (Workstream recap): three new rows — Guardrails audit
  (#155/#159/#161), Tenancy + entity stamping (#157/#164), K8s
  scheduler (#162).
  Section 19 (Docs map): add tenancy.md + scheduler-kubernetes.md
  tree entries; annotate guardrails.md and audit-logging.md with
  the latest issue refs.

No code changes; pure docs sweep.
initializ-mk added a commit that referenced this pull request Jun 15, 2026
…knowledge skill

Sweeps the doc areas mapped to the code changes on this branch
(parts 2/2b/3 of #162) plus the recently merged work that landed
on main since the last sync-docs run.

docs/core-concepts/scheduling.md
  Replace single-paragraph "Execution Details" with a backend
  comparison table + the scheduler.yaml block + the cross-link to
  the new scheduler-kubernetes.md reference. Notes that K8s mode
  defers history to the audit stream instead of writing
  SCHEDULES.md.

docs/reference/forge-yaml-schema.md
  Add the `scheduler:` block under `schedules:` documenting the
  three backend modes and the `kubernetes:` tuning fields
  (namespace, service_url, allow_dynamic, trigger_image,
  auth_secret_name).

docs/reference/cli-reference.md
  Add the `forge auth` subcommand reference (PR #168 / #162 part 1)
  with the show-token / mint-token / secret-yaml flows and the
  label-tracks-forge.yaml invariant note.

README.md
  Link scheduler-kubernetes.md from the documentation index.

.claude/skills/forge.md
  Section 9 (Scheduling): hybrid Backend interface, FileBackend /
  KubernetesBackend table, InCluster() detection, AllowDynamic
  rule, cross-link to scheduler-kubernetes.md.
  Section 11 (Build pipeline): new ScheduleManifestStage row (6a)
  documenting the cronjob/secret-template/role triplet.
  Section 13 (CLI surface): `forge auth` row.
  Section 14 (forge.yaml): scheduler block in the example yaml.
  Section 18 (Workstream recap): three new rows — Guardrails audit
  (#155/#159/#161), Tenancy + entity stamping (#157/#164), K8s
  scheduler (#162).
  Section 19 (Docs map): add tenancy.md + scheduler-kubernetes.md
  tree entries; annotate guardrails.md and audit-logging.md with
  the latest issue refs.

No code changes; pure docs sweep.
initializ-mk added a commit that referenced this pull request Jun 15, 2026
…knowledge skill

Sweeps the doc areas mapped to the code changes on this branch
(parts 2/2b/3 of #162) plus the recently merged work that landed
on main since the last sync-docs run.

docs/core-concepts/scheduling.md
  Replace single-paragraph "Execution Details" with a backend
  comparison table + the scheduler.yaml block + the cross-link to
  the new scheduler-kubernetes.md reference. Notes that K8s mode
  defers history to the audit stream instead of writing
  SCHEDULES.md.

docs/reference/forge-yaml-schema.md
  Add the `scheduler:` block under `schedules:` documenting the
  three backend modes and the `kubernetes:` tuning fields
  (namespace, service_url, allow_dynamic, trigger_image,
  auth_secret_name).

docs/reference/cli-reference.md
  Add the `forge auth` subcommand reference (PR #168 / #162 part 1)
  with the show-token / mint-token / secret-yaml flows and the
  label-tracks-forge.yaml invariant note.

README.md
  Link scheduler-kubernetes.md from the documentation index.

.claude/skills/forge.md
  Section 9 (Scheduling): hybrid Backend interface, FileBackend /
  KubernetesBackend table, InCluster() detection, AllowDynamic
  rule, cross-link to scheduler-kubernetes.md.
  Section 11 (Build pipeline): new ScheduleManifestStage row (6a)
  documenting the cronjob/secret-template/role triplet.
  Section 13 (CLI surface): `forge auth` row.
  Section 14 (forge.yaml): scheduler block in the example yaml.
  Section 18 (Workstream recap): three new rows — Guardrails audit
  (#155/#159/#161), Tenancy + entity stamping (#157/#164), K8s
  scheduler (#162).
  Section 19 (Docs map): add tenancy.md + scheduler-kubernetes.md
  tree entries; annotate guardrails.md and audit-logging.md with
  the latest issue refs.

No code changes; pure docs sweep.
initializ-mk added a commit that referenced this pull request Jun 15, 2026
…knowledge skill

Sweeps the doc areas mapped to the code changes on this branch
(parts 2/2b/3 of #162) plus the recently merged work that landed
on main since the last sync-docs run.

docs/core-concepts/scheduling.md
  Replace single-paragraph "Execution Details" with a backend
  comparison table + the scheduler.yaml block + the cross-link to
  the new scheduler-kubernetes.md reference. Notes that K8s mode
  defers history to the audit stream instead of writing
  SCHEDULES.md.

docs/reference/forge-yaml-schema.md
  Add the `scheduler:` block under `schedules:` documenting the
  three backend modes and the `kubernetes:` tuning fields
  (namespace, service_url, allow_dynamic, trigger_image,
  auth_secret_name).

docs/reference/cli-reference.md
  Add the `forge auth` subcommand reference (PR #168 / #162 part 1)
  with the show-token / mint-token / secret-yaml flows and the
  label-tracks-forge.yaml invariant note.

README.md
  Link scheduler-kubernetes.md from the documentation index.

.claude/skills/forge.md
  Section 9 (Scheduling): hybrid Backend interface, FileBackend /
  KubernetesBackend table, InCluster() detection, AllowDynamic
  rule, cross-link to scheduler-kubernetes.md.
  Section 11 (Build pipeline): new ScheduleManifestStage row (6a)
  documenting the cronjob/secret-template/role triplet.
  Section 13 (CLI surface): `forge auth` row.
  Section 14 (forge.yaml): scheduler block in the example yaml.
  Section 18 (Workstream recap): three new rows — Guardrails audit
  (#155/#159/#161), Tenancy + entity stamping (#157/#164), K8s
  scheduler (#162).
  Section 19 (Docs map): add tenancy.md + scheduler-kubernetes.md
  tree entries; annotate guardrails.md and audit-logging.md with
  the latest issue refs.

No code changes; pure docs sweep.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant