railiance-forge/docs/proposals/cust81-private-reader/target-contract-verification-plan.md
tegwick 35d966dacd docs: review seven-source reader API and bounded recovery
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
2026-10-15 01:39:13 +02:00

4.4 KiB

Forge owner contract and execution evidence boundary

Public metadata was read without credentials or mutations at https://forgejo.coulomb.social/api/v1/version and /swagger.v1.json. Both report 11.0.3+gitea-1.22.0. The matching upstream tag is contractual support; it does not establish deployed image bytes/configuration or grants. The exact public schema/source hashes are in target-api-contract-r1.json.

The current /user/repos endpoint is usable for effective grants: upstream routers/api/v1/user/repo.go defaults Collaborate=None, and models/repo/repo_list.go SearchRepositoryCondition combines ownership, UserAccessRepoCond and UserOrgTeamRepoCond. Swagger's "owns" description alone is insufficient. This corrects the initial owned-only hypothesis.

Every organization/team/member/repository/PAT list must be read completely. Upstream routers/api/v1/utils/page.go calls services/convert/utils.go ToCorrectPageSize; modules/setting/api.go defaults MaxResponseItems to 50. A request for limit100 can return50 with further pages. Read bounded pages until an empty terminal result or reconcile a valid stable X-Total-Count. Never infer completion from a short page relative to requested limit; reject duplicate identities/repeated pages, cap exhaustion and changing or invalid totals. Test page2 excluded private grant, unrelated team/token, server-cap7 and repeated/incomplete pages with synthetic fixtures.

GET token inventory supports administrator context: API router self-or-admin gate; ListAccessTokens reads ContextUser.ID and returns token_last_eight without the token. Create and DELETE additionally require Basic or reverse-proxy auth. Exact token revocation therefore needs a reviewed method compatible with that auth contract (e.g. attended admin resets only dedicated user's random memory-only password, then user Basic deletes exact reviewed tokenID). An admin PAT alone cannot satisfy the Basic-only DELETE gate. The original tokenID and ambiguity must remain durable; an observation timeout never authorizes a remint.

The intended team code-read flags and restricted private non-admin user fields exist in the observed API schema. Non-code units none are valid; upstream MinUnitAccessMode deliberately ignores none while finding a positive minimum. Bearer PAT headers are accepted by services/auth/oauth2.go parseToken. Still, only actual native fetch after approved provisioning can prove installed route behavior and consented index/entry hashes.

SEC-DSM §37 is evaluated in order:

  1. Q1: Current native HTTP interface does not demonstrate direct projected SA identity at the forge; changing visibility or transport is outside scope.
  2. Q2: Human KeyCape OIDC login is not machine-token exchange. Matching upstream authentication validates Forgejo-owned OAuth tokens; no installed trust of arbitrary KeyCape workload JWTs is established.
  3. Q3: Forgejo supports expiring OAuth access tokens, so do not state it cannot issue ephemeral credentials. Matching token grant handler accepts only authorization_code and refresh_token, not client_credentials/token_exchange. Unattended refresh requires persistent authority; no viable installed workload-native ephemeral path is demonstrated. Keep target-owner execution confirmation gate and prefer a viable higher mechanism if surfaced.
  4. Q4: Proposed bootstrap can mint a dedicated reader but is not an automatic rotation controller. No automatic90day enforcement is demonstrated.
  5. Q5: A dedicated centrally vaulted PAT is a conditional static fallback with restricted target grants and owned manual rotation/revocation. ESO15minute Bao tokens expire delivery authority, not the PAT.

Approved attended verification must quietly prove /user under Sudo is the exact restricted reader; complete identities/organizations/teams/repos/PATs; exact seven private repos, no extra private effective grants, read permission; one PAT read:repository and token_last_eight binding to custody; native Bearer fetch matching consented hashes; excluded private repo/admin/actions denial. Only booleans/counts/IDs/hashes survive. No private source/error/token output. Record GENERATED_AT, rotation due_at<=90days, target tokenID, owner/recovery task and observed custody version; retain manual policy as manual. Revalidate public version/schema before mutation and halt drift until reviewed. No such attended proof or credential approval exists in this preparation.