feat(config): routing and roster as a file, and push rights minted per Run #27

Merged
ryangr0 merged 1 commit from agent/config-and-tier2 into development 2026-07-29 19:09:25 +00:00
Owner

Two changes that were asked for directly, plus the ADR the second one needed.

CONFIGURATION MOVES OUT OF ENVIRONMENT VARIABLES.

PLOEG_TARGET_MAP, PLOEG_TEAM_MAP and PLOEG_TEAM_PLANS were three hand-rolled
DSLs with no schema, no comments and no diff worth reading. They are replaced
by one YAML file, rendered from Helm values into a ConfigMap and mounted at
/etc/ploeg/ploeg.yaml. A typo'd key now fails the boot (KnownFields) instead
of silently taking a default.

The sharper half is the magic numbers. "11/bronze=webgrip/ploeg@development"
put a Vikunja project ID into cluster config, where a bare 11 says nothing
about which board it is, cannot be reviewed, and silently routes work to the
wrong repository the day the project is rebuilt with a new id. Projects are
NAMED now:

trackers:
  vikunja:
    projects:
      - name: "Ploeg Test"
        repo: webgrip/ploeg
        branch: development

ploegd asks the tracker which id that name has at boot, logs what it resolved,
and refuses to start if the name matches nothing — with the available names in
the error, so the operator can fix it. id: remains as an escape hatch for a
board whose names are not unique. The env vars still work when the file is
absent, so this migrates one deployment at a time.

It renders to the same wire format pkg/target already parses, so there is one
routing resolver in the codebase, not two that drift.

PUSH RIGHTS ARE MINTED PER RUN (ADR-0013 tier 2, now accepted).

Tier 1 gave readers a weaker static token, which closes the hole that matters.
This closes the other one: a writer pod partitioned from ploegd keeps running
after its Lease expires, and with a shared static credential it can still push
to a branch another Run has since taken over. Now a writing Run gets a
write:repository token minted for it alone, named ploeg-run-<12hex>- so
a token in the forge UI traces to a Run, a Shift and a ticket the way the
LiteLLM alias does. Revoked on report, on lease expiry by the sweeper, and by
a boot sweep for whatever a crashed ploegd left behind — the same three-layer
shape ADR-0008 uses for spend, because that pattern is proven here and a
second novel one would be a second thing to get wrong.

If the mint fails, nothing runs: the Run is finished as a retryable infra
failure and the pod exits empty-handed, rather than proceeding with a
credential we did not intend to hand out. Readers get nothing minted at all —
no Lease, no business pushing.

The admin credential lives only in ploegd, never in a worker pod (R6). That is
a real escalation and ADR-0013 accepts it explicitly, on ADR-0008's reasoning.
Unset, everything falls back to the shared token and the worker path is
identical, so this ships dark.

Honest limitation, recorded in the code: Forgejo's token API scopes by
permission, not by repository, so the repo in the token name is audit rather
than enforcement — the bot's own repository access is still what bounds it.

ADR-0017 flipped proposed -> accepted: its implementation merged in #26.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

Two changes that were asked for directly, plus the ADR the second one needed. CONFIGURATION MOVES OUT OF ENVIRONMENT VARIABLES. PLOEG_TARGET_MAP, PLOEG_TEAM_MAP and PLOEG_TEAM_PLANS were three hand-rolled DSLs with no schema, no comments and no diff worth reading. They are replaced by one YAML file, rendered from Helm values into a ConfigMap and mounted at /etc/ploeg/ploeg.yaml. A typo'd key now fails the boot (KnownFields) instead of silently taking a default. The sharper half is the magic numbers. "11/bronze=webgrip/ploeg@development" put a Vikunja project ID into cluster config, where a bare 11 says nothing about which board it is, cannot be reviewed, and silently routes work to the wrong repository the day the project is rebuilt with a new id. Projects are NAMED now: trackers: vikunja: projects: - name: "Ploeg Test" repo: webgrip/ploeg branch: development ploegd asks the tracker which id that name has at boot, logs what it resolved, and refuses to start if the name matches nothing — with the available names in the error, so the operator can fix it. `id:` remains as an escape hatch for a board whose names are not unique. The env vars still work when the file is absent, so this migrates one deployment at a time. It renders to the same wire format pkg/target already parses, so there is one routing resolver in the codebase, not two that drift. PUSH RIGHTS ARE MINTED PER RUN (ADR-0013 tier 2, now accepted). Tier 1 gave readers a weaker static token, which closes the hole that matters. This closes the other one: a writer pod partitioned from ploegd keeps running after its Lease expires, and with a shared static credential it can still push to a branch another Run has since taken over. Now a writing Run gets a write:repository token minted for it alone, named ploeg-run-<12hex>-<repo> so a token in the forge UI traces to a Run, a Shift and a ticket the way the LiteLLM alias does. Revoked on report, on lease expiry by the sweeper, and by a boot sweep for whatever a crashed ploegd left behind — the same three-layer shape ADR-0008 uses for spend, because that pattern is proven here and a second novel one would be a second thing to get wrong. If the mint fails, nothing runs: the Run is finished as a retryable infra failure and the pod exits empty-handed, rather than proceeding with a credential we did not intend to hand out. Readers get nothing minted at all — no Lease, no business pushing. The admin credential lives only in ploegd, never in a worker pod (R6). That is a real escalation and ADR-0013 accepts it explicitly, on ADR-0008's reasoning. Unset, everything falls back to the shared token and the worker path is identical, so this ships dark. Honest limitation, recorded in the code: Forgejo's token API scopes by permission, not by repository, so the repo in the token name is audit rather than enforcement — the bot's own repository access is still what bounds it. ADR-0017 flipped proposed -> accepted: its implementation merged in #26. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(config): routing and roster as a file, and push rights minted per Run
All checks were successful
On Pull Request / checks (pull_request) Successful in 4m56s
3c455dac3b
Two changes that were asked for directly, plus the ADR the second one needed.

CONFIGURATION MOVES OUT OF ENVIRONMENT VARIABLES.

PLOEG_TARGET_MAP, PLOEG_TEAM_MAP and PLOEG_TEAM_PLANS were three hand-rolled
DSLs with no schema, no comments and no diff worth reading. They are replaced
by one YAML file, rendered from Helm values into a ConfigMap and mounted at
/etc/ploeg/ploeg.yaml. A typo'd key now fails the boot (KnownFields) instead
of silently taking a default.

The sharper half is the magic numbers. "11/bronze=webgrip/ploeg@development"
put a Vikunja project ID into cluster config, where a bare 11 says nothing
about which board it is, cannot be reviewed, and silently routes work to the
wrong repository the day the project is rebuilt with a new id. Projects are
NAMED now:

    trackers:
      vikunja:
        projects:
          - name: "Ploeg Test"
            repo: webgrip/ploeg
            branch: development

ploegd asks the tracker which id that name has at boot, logs what it resolved,
and refuses to start if the name matches nothing — with the available names in
the error, so the operator can fix it. `id:` remains as an escape hatch for a
board whose names are not unique. The env vars still work when the file is
absent, so this migrates one deployment at a time.

It renders to the same wire format pkg/target already parses, so there is one
routing resolver in the codebase, not two that drift.

PUSH RIGHTS ARE MINTED PER RUN (ADR-0013 tier 2, now accepted).

Tier 1 gave readers a weaker static token, which closes the hole that matters.
This closes the other one: a writer pod partitioned from ploegd keeps running
after its Lease expires, and with a shared static credential it can still push
to a branch another Run has since taken over. Now a writing Run gets a
write:repository token minted for it alone, named ploeg-run-<12hex>-<repo> so
a token in the forge UI traces to a Run, a Shift and a ticket the way the
LiteLLM alias does. Revoked on report, on lease expiry by the sweeper, and by
a boot sweep for whatever a crashed ploegd left behind — the same three-layer
shape ADR-0008 uses for spend, because that pattern is proven here and a
second novel one would be a second thing to get wrong.

If the mint fails, nothing runs: the Run is finished as a retryable infra
failure and the pod exits empty-handed, rather than proceeding with a
credential we did not intend to hand out. Readers get nothing minted at all —
no Lease, no business pushing.

The admin credential lives only in ploegd, never in a worker pod (R6). That is
a real escalation and ADR-0013 accepts it explicitly, on ADR-0008's reasoning.
Unset, everything falls back to the shared token and the worker path is
identical, so this ships dark.

Honest limitation, recorded in the code: Forgejo's token API scopes by
permission, not by repository, so the repo in the token name is audit rather
than enforcement — the bot's own repository access is still what bounds it.

ADR-0017 flipped proposed -> accepted: its implementation merged in #26.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ryangr0 merged commit cda509505f into development 2026-07-29 19:09:25 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/ploeg!27
No description provided.