docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
72f3a6769f
commit
3ca8bb63a2
50 changed files with 10673 additions and 0 deletions
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"remoteRef": {
|
||||
"key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"property": "FORGEJO_SOURCE_TOKEN"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
path "platform/data/workloads/reuse/reuse-surface/runtime-secrets" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/lookup-self" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/revoke-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
path "platform/data/workloads/reuse/reuse-surface/forgejo-source-read" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
---
|
||||
id: reuse-surface-private-source-reader
|
||||
revision: 2
|
||||
proposal_id: MASON-CUST81-PRIVATE-READER-R1
|
||||
demand_source: the-custodian/workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md
|
||||
consumer_repo: reuse-surface
|
||||
credential_type: forgejo-restricted-source-read-with-existing-kubernetes-eso
|
||||
status: draft
|
||||
approved_by: null
|
||||
approved_at: null
|
||||
created: "2026-10-15"
|
||||
updated: "2026-10-15"
|
||||
construction_inputs: builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
candidate_spec_sha256: 59a7334eb58135790547fab0a8c8e8debb94cc7acaae88d30654c9ae887d6732
|
||||
approved_spec_sha256: null
|
||||
filed_as: null
|
||||
---
|
||||
|
||||
# Construction plan: seven-repository hosted capability reader
|
||||
|
||||
## 1. Demand
|
||||
|
||||
The reuse-surface hub has established 63 of the 70 reviewed security capabilities.
|
||||
It needs authenticated reads of the seven remaining private repository indexes.
|
||||
The founder explicitly approved only public disclosure of the exact capability
|
||||
metadata in CUST81-PRIVATE-METADATA-DISCLOSURE-R1; repository source remains
|
||||
private. This plan proposes a dedicated repository reader for the hosted process
|
||||
and delivery through its existing namespace-bound ESO identity. Credential access
|
||||
and live changes require their own reviewed exact-revision approval.
|
||||
|
||||
Preparation source is CUST81-PRIVATE-READER-PROVISIONING-PREPARATION-R1, SHA256
|
||||
`7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99`.
|
||||
Consent receipt SHA256 is
|
||||
`4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52`;
|
||||
disclosure packet SHA256 is
|
||||
`46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35`.
|
||||
The bundled construction-inputs retain all seven consented commits, index hashes
|
||||
and native raw API URLs. These references contain no credential values.
|
||||
|
||||
The delegated ops-mason preparation worker has no own supervision record or
|
||||
survey token. It is supervised for preparation only and borrows no custodian
|
||||
agent authority. No live OpenBao, Forgejo, Kubernetes or credential read was
|
||||
performed. This plan is deliberately **draft**, not reviewed or executable.
|
||||
|
||||
## 2. Existing-structure survey
|
||||
|
||||
### Local evidence; live survey incomplete
|
||||
|
||||
A live `scripts/plan-survey.py` result does not exist: no scoped ops-mason-survey
|
||||
grant was supplied. Do not treat source files as installed state. Before review,
|
||||
obtain an owner-scoped metadata survey with exact path/policy and Kubernetes-role
|
||||
arguments, or a platform-owned sanitized equivalent. Refused queries stay
|
||||
incomplete rather than being described as absence. No operator/admin session may
|
||||
be substituted for the ops-mason survey grant.
|
||||
|
||||
Local source observations from the pinned preparation and checked files:
|
||||
|
||||
- `railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl`
|
||||
SHA256 `fab29572fe82fc6674837e430f616409070f97a3867a411b7e21b5141dfaba20`
|
||||
grants the existing runtime-secrets data path and lookup/revoke-self endpoints.
|
||||
- The caller policy `workload-kv-read-reuse-surface-runtime`, SHA256
|
||||
`53dd5eecc94fd8d7fc281a858f5d9fc27c8c895fd79161fd113cd3a1387a1257`,
|
||||
grants the existing runtime-secrets entry plus self-lifecycle/capability checks;
|
||||
it does not grant the proposed new source-reader path.
|
||||
- ClusterSecretStore `openbao-reuse` uses Kubernetes role
|
||||
`reuse-surface-runtime-eso`, SA `reuse/reuse-surface-eso`, audience `openbao`,
|
||||
conditions limited to namespace `reuse`. No new static ESO token is proposed.
|
||||
- `railiance-apps/manifests/reuse-surface-runtime-externalsecret.yaml` already maps
|
||||
writer/webhook fields to `reuse/reuse-surface-env`; Deployment uses envFrom.
|
||||
Its comments still describe a retired static ESO token; those comments must not
|
||||
be taken as a procedure or used to mint one. Apps owns correction on integration.
|
||||
- CCR-2026-0031 and `provision-core-hub-forgejo-source.py` demonstrate dedicated
|
||||
`read:repository` PAT custody. That script explicitly sets all-repository team
|
||||
membership and must **not** be executed unchanged for this demand.
|
||||
- Catalog `reuse-surface-hub-write-token` describes the caller writer lane;
|
||||
`forgejo-admin-api-token` is privileged bootstrap authority. Neither is the
|
||||
private-index consumer credential. The preparation found no dedicated private
|
||||
reader lane; installed absence has not been surveyed.
|
||||
|
||||
**Does an existing lane satisfy this demand?** Existing workload identity and
|
||||
ESO delivery can be reused. Existing writer credentials and all-repository
|
||||
source readers cannot satisfy the exact-seven grant. Propose a dedicated target
|
||||
identity and new exact KV entry, with one stanza added to the existing ESO policy.
|
||||
A differing existing user/team/path/policy must halt for reconciliation before
|
||||
any mutation. Matching names alone do not establish matching effective scope.
|
||||
|
||||
## 3. Proposed changes
|
||||
|
||||
### SEC-DSM §37 decision procedure, in order
|
||||
|
||||
1. **Q1 — native identity:** the current native raw HTTP fetch uses an
|
||||
Authorization credential; Kubernetes SA identity alone is not demonstrated
|
||||
as accepted by this Forgejo route. Candidate answer: no for the current
|
||||
consumer interface, subject to Forgejo/reuse owner confirmation. Anonymous
|
||||
access would require private visibility change and does not satisfy demand.
|
||||
2. **Q2 — federation:** human KeyCape OIDC login does not prove a machine-token
|
||||
exchange for these raw API requests. No workload federation exchange for this
|
||||
target is evidenced by the local source review. Candidate answer: no viable
|
||||
installed interface demonstrated; Forgejo owner must confirm or supply a
|
||||
higher mechanism. If supported reliably, revise to federation before approval.
|
||||
3. **Q3 — ephemeral target credential:** the local CCR-0031 precedent states PATs
|
||||
have no server-side expiry. No usable ephemeral Forgejo issuance/revocation
|
||||
service is evidenced. Candidate answer: no mechanism demonstrated, pending
|
||||
target-owner confirmation. Do not add a broker just to label a static PAT
|
||||
dynamic (DSM-TRANS-003). ESO's 15-minute Bao token does not expire the PAT.
|
||||
4. **Q4 — automatic rotation:** do not reuse an existing broader PAT. No automated
|
||||
rotation job for this proposed dedicated identity is evidenced. Provisioning
|
||||
capability is not automatic lifecycle enforcement. Answer: not demonstrated;
|
||||
proposed attended rotation is explicitly manual, owned and dated.
|
||||
5. **Q5 — residual static credential:** conditional on Q1–Q3 owner confirmation,
|
||||
use a centrally governed dedicated PAT, OpenBao as custody authority, Forgejo
|
||||
as grant/revocation authority, ESO/Kubernetes/env as delivery copies. Level 5
|
||||
in §5, **centrally managed static secret**, not level 3 or 4. No AppRole is
|
||||
needed: the existing ESO projected workload identity authenticates to Bao.
|
||||
|
||||
These answers follow DSM-ARCH-001–004, DSM-TRANS-002/003, DSM-K8S-001–004 and
|
||||
§35/36: use existing projected SA identity for delivery, avoid adding a static
|
||||
ESO token or broker, and distinguish central custody from delivery copies.
|
||||
Unconfirmed Q1–Q3 are explicit review gates, not asserted facts.
|
||||
|
||||
### Exact object changes and boundaries
|
||||
|
||||
- **create** restricted private non-admin Forgejo user `reuse-surface-source`;
|
||||
**create** org team `coulomb/reuse-surface-source-readers`. Dedicated target
|
||||
identity replaces unsuitable broad readers identified in §2. Team permission
|
||||
`read`, `includes_all_repositories=false`, `can_create_org_repo=false`, only
|
||||
`repo.code=read`; all non-code units `none`. Team members exactly this user.
|
||||
- **create** team membership for exactly `coulomb/rapp-openbao`,
|
||||
`coulomb/rapp-policy-nexus`, `coulomb/rapp-postgres`, `coulomb/rapp-sbom-nexus`,
|
||||
`coulomb/rapp-telemetry`, `coulomb/rapp-tenant-engine`, `coulomb/rapp-user-engine`.
|
||||
Follow all API pages and inspect effective user grants, other teams, direct
|
||||
collaborator access, organisation membership and repository ownership. Adding
|
||||
seven repositories without checking extra effective grants is insufficient.
|
||||
Existing identity/team with differing configuration is a conflict, not permission
|
||||
to remove unrelated access silently. Reconciliation needs a revised plan.
|
||||
- **create** dedicated PAT named `reuse-surface-source-read-<UTC-attempt>` with
|
||||
scopes exactly `[read:repository]`. Platform-owned attended helper holds values
|
||||
in memory, suppresses every raw response/error and returns token IDs only.
|
||||
Password used for service bootstrap is random memory-only, never persisted
|
||||
in evidence or reused as workload authentication. Prevent unattended password
|
||||
login and recovery escalation according to Forgejo owner review.
|
||||
- **create** KV-v2 entry `platform/workloads/reuse/reuse-surface/forgejo-source-read`,
|
||||
field `FORGEJO_SOURCE_TOKEN`. Platform alone provisions value through its
|
||||
reviewed silent custody route from stdin; ops-mason does not handle the value.
|
||||
It must retain accessor/token ID and custody version in sanitized lifecycle
|
||||
inventory. A new path separates private reader from attended writer callers.
|
||||
- **modify** only existing policy `workload-kv-read-reuse-surface-runtime-eso` by
|
||||
adding `read` on exact
|
||||
`platform/data/workloads/reuse/reuse-surface/forgejo-source-read`.
|
||||
Candidate full file is bundled, SHA256
|
||||
`788bb9c49ab571e522beeecf7a5e29d1c79c842a84fe6d801978655facd3b5bd`.
|
||||
No glob, metadata grant, other sibling, caller writer policy, OIDC binding,
|
||||
AppRole or Kubernetes auth role change. Verify role/token is still 15m and
|
||||
bound to exact SA/namespace/audience; source configuration is not live proof.
|
||||
- **modify** existing `reuse/reuse-surface-runtime` ExternalSecret by one data
|
||||
item: secretKey `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN`, remoteRef key
|
||||
`workloads/reuse/reuse-surface/forgejo-source-read`, property
|
||||
`FORGEJO_SOURCE_TOKEN`. Bundled JSON item is a proposal for apps integration,
|
||||
not a standalone apply object. Preserve existing writer and webhook fields.
|
||||
- **modify** native fetch code/image through reuse and apps review: reuse owner
|
||||
identified both Authorization header forwarding on redirects and registration-
|
||||
controlled auth_env/URL credential exfiltration in the current native fetch.
|
||||
Do not project the PAT until the full reviewed fix is image-digest pinned and
|
||||
runtime authorization plus redirect credential isolation are verified.
|
||||
Then deliver through existing envFrom and an apps-approved controlled rollout.
|
||||
Image change is explicit; preserve resource bounds unless separately reviewed.
|
||||
Rollout must account for SQLite/PVC and single-node headroom before approval.
|
||||
- **modify** owner-controlled runtime config `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST`
|
||||
to map `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` to the exact fourteen consented
|
||||
pinned API URL strings: seven indexes and seven capability entries. Candidate
|
||||
config is `builds/reuse-surface-private-source-reader/auth-source-allowlist.json`;
|
||||
registrations cannot set or widen this runtime authority. The patched consumer
|
||||
checks exact URL membership **before credential lookup**, requires HTTPS and
|
||||
refuses authenticated redirects including same-origin path changes. Apps/reuse
|
||||
must safely inventory existing authenticated registrations (auth_env and URL
|
||||
metadata only), review which destinations remain legitimate, and migrate their
|
||||
mappings before fail-closed rollout. Inventory is not blanket consent. The new
|
||||
reader mapping stays exact14; any existing legitimate other mapping needs its
|
||||
own owner-reviewed authorization. Missing/invalid mappings fail closed.
|
||||
- **create** proposed ops-warden pointer `reuse-surface-forgejo-source-read` in
|
||||
draft status after construction, owner `railiance-platform`, non-SSH route
|
||||
only (`warden_executes:false`). Runtime reader is the hub, not warden.
|
||||
Activation remains ops-warden's independent verified catalog review.
|
||||
|
||||
### Full consumer candidate pins
|
||||
|
||||
Reuse owner provided full patch SHA256
|
||||
`f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e`,
|
||||
fixed `federation.py` SHA256
|
||||
`dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd`,
|
||||
native verification helper `private_source_verify.py` SHA256
|
||||
`c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c`,
|
||||
and owner runtime allowlist SHA256
|
||||
`e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286`.
|
||||
The ops-mason bundled compact allowlist has the same exact env and fourteen URL
|
||||
sets; serialization/order differs. The construction inputs retain absolute
|
||||
isolated-owner artifact references for integration. Owner reports focused30pass
|
||||
and whole226pass with one isolated-relative-path failure; this is not native
|
||||
runtime proof. Fixed image digest and owner execution review remain pending.
|
||||
|
||||
### Revision preservation
|
||||
|
||||
R2 refines the same proposal and trial; R1 is preserved under custodian
|
||||
`owner-assembly/mason-r1/` and `/tmp/cust81-mason-reader-r1-snapshot`. R1 candidate
|
||||
spec digest was `0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980`.
|
||||
Neither revision was approved or executed. The full consumer fix and runtime
|
||||
allowlist supersede the earlier redirect-only image gate. Approval, when sought,
|
||||
must bind this final reviewed revision, its consumer patch, runtime config and
|
||||
image; do not create another scored trial from this refinement.
|
||||
|
||||
### Residual-secret lifecycle and elimination
|
||||
|
||||
Credential owner is railiance-platform; Forgejo owns target grants; reuse-surface
|
||||
is the only workload consumer. Authority exposes **entire source code of the
|
||||
seven repositories**, not just registry files. Metadata disclosure approval
|
||||
alone does not authorize this larger repository-read grant.
|
||||
|
||||
Known intended copies: OpenBao KV current version and retained versions/backups;
|
||||
Kubernetes Secret in `reuse` and etcd backups; hub process environment; transient
|
||||
minting/delivery memory. Every namespace actor allowed to attach the ESO store or
|
||||
reference the materialized Secret is an indirect consumer/exposure path and must
|
||||
be reviewed; namespace conditions are not per-pod isolation. No CI/chat/local
|
||||
credential file copy is allowed. Avoid Secret last-applied annotation copies.
|
||||
|
||||
Proposed lifetime policy is attended rotation **every 90 days**, and immediately
|
||||
on compromise, owner change, consumer retirement or scope drift. This is a
|
||||
recommended stricter policy than CCR-0031's 180-day precedent; it is not a claim
|
||||
of automated enforcement or PAT native expiry. Record issuance and next-due UTC
|
||||
in platform inventory, with an assigned owner reminder before activation.
|
||||
Rotation: mint one replacement with the same checked grant; compare scopes and
|
||||
all effective grants; write it as a new KV version without changing other fields;
|
||||
wait for ESO, roll the reviewed fixed image, verify all seven native fetches, then revoke old
|
||||
PAT by recorded ID. Replacement overlap must end within 24 hours. Failed or
|
||||
ambiguous replacement issuance is reconciled by token-name/ID inventory before
|
||||
retry; no blind minting. Review retained KV versions/backups under platform's
|
||||
retention policy; obsolete token copies are harmless only after target revocation.
|
||||
|
||||
Immediate revocation deletes the dedicated PAT by ID, then removes/disables only
|
||||
this identity's team grants as approved. Compromise response reviews Forgejo
|
||||
access/audit metadata and scans owned repositories and approved logs for leaks
|
||||
without placing tokens in prompts. Use secret scanners/redactors and suppressed
|
||||
negative probes. Availability strategy fails private refresh closed and marks
|
||||
consented cache stale; public discovery continues. Never substitute privileged
|
||||
personal/admin/core-hub credentials during issuer outage.
|
||||
|
||||
Elimination depends on Forgejo/native federation or an operationally viable
|
||||
short-lived service credential, plus native consumer support. Revisit at rotation
|
||||
and at target upgrades; retain a live owner record for that engineering debt.
|
||||
|
||||
## 4. Review notes (ops-mason phase 2, incomplete)
|
||||
|
||||
Reuse existing delivery identity and stores rather than introduce AppRole/static
|
||||
ESO authority. The new PAT is isolated; caller writer policy stays byte-identical.
|
||||
The proposed full ESO policy preserves every original byte before one added exact
|
||||
read stanza; integration must compare current live and source digests. If policy
|
||||
changed, rebase, review and pin a fresh digest rather than overwrite it.
|
||||
|
||||
Existing `ops_mason.executor` engines do not express Forgejo user/team/PAT and
|
||||
shared-policy extension. Do not mislabel this JSON as a supported build_spec or
|
||||
pass it to a single-KV builder: that could replace existing policy. Candidate
|
||||
spec digest is SHA256 of UTF-8 sorted compact JSON with allow_nan=false. It
|
||||
binds scope; it is neither approval nor an executable adapter. Platform must
|
||||
allocate CCR, own a silent idempotent provisioner and pin all execution/artifact
|
||||
inputs before phase 3. Approval of a construction plan does not supply a token.
|
||||
|
||||
This draft is not yet phase-3 ready. Gaps: scoped live structure survey, target
|
||||
Q1–Q3 confirmation, effective-access enumeration design, reviewed platform CCR
|
||||
and script, consumer raw-route/Bearer behavior, full authorization/redirect-fixed image and runtime allowlist proof plan, apps exact deployment
|
||||
and headroom/PVC rollback plan, independent owner reviews. No owner review is
|
||||
written on another repo's behalf. Only ops-mason's own preparation findings are
|
||||
recorded here. No own autopilot grant or monetary risk limit is claimed.
|
||||
|
||||
## 5. Executive summary (candidate; not yet ready for approval)
|
||||
|
||||
1. **One-line ask.** Give the hosted capability hub a dedicated read-only account
|
||||
for the seven private repositories whose capability metadata you consented
|
||||
to publish, delivered using the existing workload-identity path.
|
||||
2. **Who/what gets access.** Forgejo user `reuse-surface-source` obtains the
|
||||
seven-repository read grant; the existing `reuse-surface-runtime-eso` identity
|
||||
reads one additional exact custody entry for the hub. Namespace actors able
|
||||
to reuse the delivery store/Secret remain an exposure consideration to review.
|
||||
3. **To what.** Read-only source access across the seven named private repositories,
|
||||
with no write/admin/non-code-unit grants. The hub publishes only the pinned
|
||||
consented capability metadata; repository visibility stays private.
|
||||
4. **For how long.** Bao delivery tokens remain 15 minutes, but the Forgejo PAT
|
||||
has no expiry demonstrated. Proposed attended rotation is every 90 days with
|
||||
immediate revocation on compromise; this distinction must be accepted explicitly.
|
||||
5. **Blast radius if the credential leaks.** Entire private source of those seven
|
||||
repositories can be read. The token must have no additional private-repository
|
||||
access and cannot write. Metadata disclosure consent is narrower than that read
|
||||
grant. Availability impact of controlled rollout remains owner-reviewed.
|
||||
6. **Cost to reverse.** Revoke one PAT, remove only its dedicated grants, remove
|
||||
one new ESO data item and one new policy stanza, and roll the reviewed fixed image.
|
||||
Reconcile private registrations from the exact attempt; preserve the existing
|
||||
public capabilities and writer/webhook keys. No additional paid infrastructure
|
||||
is proposed (incremental estimate EUR 0); labor and credible loss remain unpriced,
|
||||
so this stays supervised.
|
||||
|
||||
After the missing reviews and executable inputs are attached, the founder can
|
||||
**approve**, **reject**, or **send back to phase 1**. No founder disposition is
|
||||
recorded for this credential proposal. Existing metadata consent is retained as
|
||||
separate evidence and does not set status approved.
|
||||
|
||||
## 6. Proposed verification and rollback contract
|
||||
|
||||
This section describes future acceptance, **not a build result**. Phase 4 has not
|
||||
run and no success evidence or active catalog entry exists.
|
||||
|
||||
Before changes: exact CCR/plan/script/manifest digests, owner reviews and founder
|
||||
approved revision must match; metadata survey proves current path/policy/role;
|
||||
source/consent pins match; all existing user/team conflicts resolved; previous
|
||||
policy and deployment revision retained; apps runs dry-run and client diff,
|
||||
confirms target node and headroom/PVC strategy. Never read or print Secret objects.
|
||||
|
||||
Before projecting any PAT into the hub: reuse owner patch must close native
|
||||
both redirect Authorization forwarding and registration-controlled auth_env/URL
|
||||
exfiltration. Tests must prove unlisted URL refusal before credential lookup and
|
||||
no redirect credential forwarding. Apps must pin the full fixed image and exact
|
||||
runtime allowlist configuration; review/migrate existing authenticated sources
|
||||
before rollout. The earlier same-image and redirect-only assumptions are
|
||||
superseded; full fixed image/source/patch and owner-runtime config approval remain
|
||||
pending. The candidate allowlist encodes the consent packet exact fourteen URLs.
|
||||
|
||||
After target issuance, scope metadata must be exactly read:repository, team repo
|
||||
set exactly seven and complete effective grants enumerated. Choose a known private
|
||||
outside repo by target-owner metadata and prove denial without body retention.
|
||||
Use safe read-only negative admin/non-code routes; never try writes as a negative
|
||||
probe because unexpected privilege could make them succeed. Verify absence of
|
||||
write authority through scope and effective permissions. Missing auth/anonymous
|
||||
private raw reads fail closed, never cached as fresh successes.
|
||||
|
||||
Bao positive receipt proves ESO role exact path read; negative receipts prove
|
||||
default identity and dedicated caller writer cannot read the new path, ESO cannot
|
||||
read an unrelated sibling, and wrong SA/namespace/audience cannot authenticate.
|
||||
These are effective token-specific checks, not a test on an admin session.
|
||||
Owner suppressing helper checks ESO Ready and materialized key presence only.
|
||||
|
||||
In the actual running hub, native `fetch_remote_index_text` must fetch all seven
|
||||
pinned URLs using `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` and native header code;
|
||||
validate schema, exact index hashes, exact expected IDs and relative entry handling
|
||||
without printing private bodies. Local curl success is insufficient for native
|
||||
runtime verification. Then register seven pinned sources with the approved writer
|
||||
lane and force fresh composition; prove all 70 reviewed IDs exactly once, no stale
|
||||
private fallback and matching metadata. Public URLs/entries and source bytes must
|
||||
stay inside disclosure consent; new commits need renewed metadata review.
|
||||
|
||||
Rollback order: immediately revoke dedicated PAT; disable only new private
|
||||
registrations after reconciling ambiguous requests; remove/revert exact new
|
||||
ESO data item and policy stanza through owner paths; remove only the new
|
||||
materialized token key with a suppressing helper, preserve writer/webhook keys;
|
||||
controlled approved-image rollout. A previously vulnerable image must never
|
||||
receive the new reader key; verify key removal before restoring such an image.
|
||||
Restore previous pinned policy/deployment revision
|
||||
when applicable. Retain partial, failed and unverified outcomes and token IDs.
|
||||
Do not delete the shared Secret/store/role, overwrite unrelated policy changes,
|
||||
or remove all public registrations. Restore only the new reader runtime mapping,
|
||||
preserving separately reviewed existing authenticated-source mappings. No transactional rollback is claimed.
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
id: MASON-WP-0013
|
||||
type: workplan
|
||||
title: "Construct the seven-repository reuse-surface source reader"
|
||||
domain: infotech
|
||||
repo: ops-mason
|
||||
status: proposed
|
||||
owner: codex
|
||||
topic_slug: netkingdom-security-architecture
|
||||
created: "2026-10-15"
|
||||
updated: "2026-10-15"
|
||||
flavor: implementation
|
||||
depends_on: []
|
||||
---
|
||||
|
||||
# Seven-repository hosted reader
|
||||
|
||||
Demand: CUST-WP-0081; metadata disclosure consent is recorded separately. This
|
||||
workplan is prepared in an isolated owner checkout and is not yet integrated,
|
||||
synced, approved or executed. No production or credential authority is inferred.
|
||||
|
||||
## Prepare the construction bundle
|
||||
|
||||
```task
|
||||
id: MASON-WP-0013-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Prepared `plans/reuse-surface-private-source-reader.md` and
|
||||
`builds/reuse-surface-private-source-reader/` with exact-seven scope, candidate
|
||||
ESO policy, data item, canonical candidate specification digest and SEC-DSM
|
||||
hierarchy/lifecycle analysis. R2 refines the same proposal: full consumer
|
||||
auth_env/URL authorization and redirect fix plus exact14 runtime allowlist gate,
|
||||
with R1 evidence preserved. Plan remains draft because required live survey
|
||||
and independent mechanism/owner reviews are missing.
|
||||
|
||||
## Complete scoped survey and construction review
|
||||
|
||||
```task
|
||||
id: MASON-WP-0013-T02
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Requires legitimate own metadata-only survey grant or sanitized platform-owned
|
||||
survey. Confirm current exact path/policy/role and matching-existing identity
|
||||
handling; Forgejo owner confirms Q1–Q3 hierarchy choices. Review effective grants
|
||||
across all API pages, secret lifecycle, negative tests and caller/ESO separation.
|
||||
Do not use operator tokens or another agent supervision record.
|
||||
|
||||
## Bind owner CCR and executable inputs to founder decision
|
||||
|
||||
```task
|
||||
id: MASON-WP-0013-T03
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Platform allocates CCR and reviewed silent adapter; apps owns concrete rollout
|
||||
and rollback artifacts including full fixed image, runtime-controlled allowlist
|
||||
and reviewed migration of existing authenticated sources; reuse owns native fetch/public metadata validation;
|
||||
warden owns pointer review. Obtain separate founder decision on exact credential
|
||||
and live-change revision/digests. Existing disclosure consent cannot approve
|
||||
repository-wide credential grant. Requires T02.
|
||||
|
||||
## Verify the authorized owner build and routing handoff
|
||||
|
||||
```task
|
||||
id: MASON-WP-0013-T04
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
After T03, founder/platform privileged path executes, ops-mason observes only
|
||||
through scoped permitted metadata. Preserve outcome/rollback receipts, complete
|
||||
native seven-index positive and least-privilege negative checks, propose draft
|
||||
catalog route and hand 70-ID fresh audit evidence to CUST-WP-0081. Never publish
|
||||
success before actual receipt or finish while unresolved lifecycle work is only
|
||||
in prose.
|
||||
|
|
@ -0,0 +1,781 @@
|
|||
diff --git a/plans/reuse-surface-private-source-reader.md b/plans/reuse-surface-private-source-reader.md
|
||||
new file mode 100644
|
||||
index 0000000..0ac705b
|
||||
--- /dev/null
|
||||
+++ b/plans/reuse-surface-private-source-reader.md
|
||||
@@ -0,0 +1,357 @@
|
||||
+---
|
||||
+id: reuse-surface-private-source-reader
|
||||
+revision: 2
|
||||
+proposal_id: MASON-CUST81-PRIVATE-READER-R1
|
||||
+demand_source: the-custodian/workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md
|
||||
+consumer_repo: reuse-surface
|
||||
+credential_type: forgejo-restricted-source-read-with-existing-kubernetes-eso
|
||||
+status: draft
|
||||
+approved_by: null
|
||||
+approved_at: null
|
||||
+created: "2026-10-15"
|
||||
+updated: "2026-10-15"
|
||||
+construction_inputs: builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
+candidate_spec_sha256: 59a7334eb58135790547fab0a8c8e8debb94cc7acaae88d30654c9ae887d6732
|
||||
+approved_spec_sha256: null
|
||||
+filed_as: null
|
||||
+---
|
||||
+
|
||||
+# Construction plan: seven-repository hosted capability reader
|
||||
+
|
||||
+## 1. Demand
|
||||
+
|
||||
+The reuse-surface hub has established 63 of the 70 reviewed security capabilities.
|
||||
+It needs authenticated reads of the seven remaining private repository indexes.
|
||||
+The founder explicitly approved only public disclosure of the exact capability
|
||||
+metadata in CUST81-PRIVATE-METADATA-DISCLOSURE-R1; repository source remains
|
||||
+private. This plan proposes a dedicated repository reader for the hosted process
|
||||
+and delivery through its existing namespace-bound ESO identity. Credential access
|
||||
+and live changes require their own reviewed exact-revision approval.
|
||||
+
|
||||
+Preparation source is CUST81-PRIVATE-READER-PROVISIONING-PREPARATION-R1, SHA256
|
||||
+`7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99`.
|
||||
+Consent receipt SHA256 is
|
||||
+`4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52`;
|
||||
+disclosure packet SHA256 is
|
||||
+`46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35`.
|
||||
+The bundled construction-inputs retain all seven consented commits, index hashes
|
||||
+and native raw API URLs. These references contain no credential values.
|
||||
+
|
||||
+The delegated ops-mason preparation worker has no own supervision record or
|
||||
+survey token. It is supervised for preparation only and borrows no custodian
|
||||
+agent authority. No live OpenBao, Forgejo, Kubernetes or credential read was
|
||||
+performed. This plan is deliberately **draft**, not reviewed or executable.
|
||||
+
|
||||
+## 2. Existing-structure survey
|
||||
+
|
||||
+### Local evidence; live survey incomplete
|
||||
+
|
||||
+A live `scripts/plan-survey.py` result does not exist: no scoped ops-mason-survey
|
||||
+grant was supplied. Do not treat source files as installed state. Before review,
|
||||
+obtain an owner-scoped metadata survey with exact path/policy and Kubernetes-role
|
||||
+arguments, or a platform-owned sanitized equivalent. Refused queries stay
|
||||
+incomplete rather than being described as absence. No operator/admin session may
|
||||
+be substituted for the ops-mason survey grant.
|
||||
+
|
||||
+Local source observations from the pinned preparation and checked files:
|
||||
+
|
||||
+- `railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl`
|
||||
+ SHA256 `fab29572fe82fc6674837e430f616409070f97a3867a411b7e21b5141dfaba20`
|
||||
+ grants the existing runtime-secrets data path and lookup/revoke-self endpoints.
|
||||
+- The caller policy `workload-kv-read-reuse-surface-runtime`, SHA256
|
||||
+ `53dd5eecc94fd8d7fc281a858f5d9fc27c8c895fd79161fd113cd3a1387a1257`,
|
||||
+ grants the existing runtime-secrets entry plus self-lifecycle/capability checks;
|
||||
+ it does not grant the proposed new source-reader path.
|
||||
+- ClusterSecretStore `openbao-reuse` uses Kubernetes role
|
||||
+ `reuse-surface-runtime-eso`, SA `reuse/reuse-surface-eso`, audience `openbao`,
|
||||
+ conditions limited to namespace `reuse`. No new static ESO token is proposed.
|
||||
+- `railiance-apps/manifests/reuse-surface-runtime-externalsecret.yaml` already maps
|
||||
+ writer/webhook fields to `reuse/reuse-surface-env`; Deployment uses envFrom.
|
||||
+ Its comments still describe a retired static ESO token; those comments must not
|
||||
+ be taken as a procedure or used to mint one. Apps owns correction on integration.
|
||||
+- CCR-2026-0031 and `provision-core-hub-forgejo-source.py` demonstrate dedicated
|
||||
+ `read:repository` PAT custody. That script explicitly sets all-repository team
|
||||
+ membership and must **not** be executed unchanged for this demand.
|
||||
+- Catalog `reuse-surface-hub-write-token` describes the caller writer lane;
|
||||
+ `forgejo-admin-api-token` is privileged bootstrap authority. Neither is the
|
||||
+ private-index consumer credential. The preparation found no dedicated private
|
||||
+ reader lane; installed absence has not been surveyed.
|
||||
+
|
||||
+**Does an existing lane satisfy this demand?** Existing workload identity and
|
||||
+ESO delivery can be reused. Existing writer credentials and all-repository
|
||||
+source readers cannot satisfy the exact-seven grant. Propose a dedicated target
|
||||
+identity and new exact KV entry, with one stanza added to the existing ESO policy.
|
||||
+A differing existing user/team/path/policy must halt for reconciliation before
|
||||
+any mutation. Matching names alone do not establish matching effective scope.
|
||||
+
|
||||
+## 3. Proposed changes
|
||||
+
|
||||
+### SEC-DSM §37 decision procedure, in order
|
||||
+
|
||||
+1. **Q1 — native identity:** the current native raw HTTP fetch uses an
|
||||
+ Authorization credential; Kubernetes SA identity alone is not demonstrated
|
||||
+ as accepted by this Forgejo route. Candidate answer: no for the current
|
||||
+ consumer interface, subject to Forgejo/reuse owner confirmation. Anonymous
|
||||
+ access would require private visibility change and does not satisfy demand.
|
||||
+2. **Q2 — federation:** human KeyCape OIDC login does not prove a machine-token
|
||||
+ exchange for these raw API requests. No workload federation exchange for this
|
||||
+ target is evidenced by the local source review. Candidate answer: no viable
|
||||
+ installed interface demonstrated; Forgejo owner must confirm or supply a
|
||||
+ higher mechanism. If supported reliably, revise to federation before approval.
|
||||
+3. **Q3 — ephemeral target credential:** the local CCR-0031 precedent states PATs
|
||||
+ have no server-side expiry. No usable ephemeral Forgejo issuance/revocation
|
||||
+ service is evidenced. Candidate answer: no mechanism demonstrated, pending
|
||||
+ target-owner confirmation. Do not add a broker just to label a static PAT
|
||||
+ dynamic (DSM-TRANS-003). ESO's 15-minute Bao token does not expire the PAT.
|
||||
+4. **Q4 — automatic rotation:** do not reuse an existing broader PAT. No automated
|
||||
+ rotation job for this proposed dedicated identity is evidenced. Provisioning
|
||||
+ capability is not automatic lifecycle enforcement. Answer: not demonstrated;
|
||||
+ proposed attended rotation is explicitly manual, owned and dated.
|
||||
+5. **Q5 — residual static credential:** conditional on Q1–Q3 owner confirmation,
|
||||
+ use a centrally governed dedicated PAT, OpenBao as custody authority, Forgejo
|
||||
+ as grant/revocation authority, ESO/Kubernetes/env as delivery copies. Level 5
|
||||
+ in §5, **centrally managed static secret**, not level 3 or 4. No AppRole is
|
||||
+ needed: the existing ESO projected workload identity authenticates to Bao.
|
||||
+
|
||||
+These answers follow DSM-ARCH-001–004, DSM-TRANS-002/003, DSM-K8S-001–004 and
|
||||
+§35/36: use existing projected SA identity for delivery, avoid adding a static
|
||||
+ESO token or broker, and distinguish central custody from delivery copies.
|
||||
+Unconfirmed Q1–Q3 are explicit review gates, not asserted facts.
|
||||
+
|
||||
+### Exact object changes and boundaries
|
||||
+
|
||||
+- **create** restricted private non-admin Forgejo user `reuse-surface-source`;
|
||||
+ **create** org team `coulomb/reuse-surface-source-readers`. Dedicated target
|
||||
+ identity replaces unsuitable broad readers identified in §2. Team permission
|
||||
+ `read`, `includes_all_repositories=false`, `can_create_org_repo=false`, only
|
||||
+ `repo.code=read`; all non-code units `none`. Team members exactly this user.
|
||||
+- **create** team membership for exactly `coulomb/rapp-openbao`,
|
||||
+ `coulomb/rapp-policy-nexus`, `coulomb/rapp-postgres`, `coulomb/rapp-sbom-nexus`,
|
||||
+ `coulomb/rapp-telemetry`, `coulomb/rapp-tenant-engine`, `coulomb/rapp-user-engine`.
|
||||
+ Follow all API pages and inspect effective user grants, other teams, direct
|
||||
+ collaborator access, organisation membership and repository ownership. Adding
|
||||
+ seven repositories without checking extra effective grants is insufficient.
|
||||
+ Existing identity/team with differing configuration is a conflict, not permission
|
||||
+ to remove unrelated access silently. Reconciliation needs a revised plan.
|
||||
+- **create** dedicated PAT named `reuse-surface-source-read-<UTC-attempt>` with
|
||||
+ scopes exactly `[read:repository]`. Platform-owned attended helper holds values
|
||||
+ in memory, suppresses every raw response/error and returns token IDs only.
|
||||
+ Password used for service bootstrap is random memory-only, never persisted
|
||||
+ in evidence or reused as workload authentication. Prevent unattended password
|
||||
+ login and recovery escalation according to Forgejo owner review.
|
||||
+- **create** KV-v2 entry `platform/workloads/reuse/reuse-surface/forgejo-source-read`,
|
||||
+ field `FORGEJO_SOURCE_TOKEN`. Platform alone provisions value through its
|
||||
+ reviewed silent custody route from stdin; ops-mason does not handle the value.
|
||||
+ It must retain accessor/token ID and custody version in sanitized lifecycle
|
||||
+ inventory. A new path separates private reader from attended writer callers.
|
||||
+- **modify** only existing policy `workload-kv-read-reuse-surface-runtime-eso` by
|
||||
+ adding `read` on exact
|
||||
+ `platform/data/workloads/reuse/reuse-surface/forgejo-source-read`.
|
||||
+ Candidate full file is bundled, SHA256
|
||||
+ `788bb9c49ab571e522beeecf7a5e29d1c79c842a84fe6d801978655facd3b5bd`.
|
||||
+ No glob, metadata grant, other sibling, caller writer policy, OIDC binding,
|
||||
+ AppRole or Kubernetes auth role change. Verify role/token is still 15m and
|
||||
+ bound to exact SA/namespace/audience; source configuration is not live proof.
|
||||
+- **modify** existing `reuse/reuse-surface-runtime` ExternalSecret by one data
|
||||
+ item: secretKey `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN`, remoteRef key
|
||||
+ `workloads/reuse/reuse-surface/forgejo-source-read`, property
|
||||
+ `FORGEJO_SOURCE_TOKEN`. Bundled JSON item is a proposal for apps integration,
|
||||
+ not a standalone apply object. Preserve existing writer and webhook fields.
|
||||
+- **modify** native fetch code/image through reuse and apps review: reuse owner
|
||||
+ identified both Authorization header forwarding on redirects and registration-
|
||||
+ controlled auth_env/URL credential exfiltration in the current native fetch.
|
||||
+ Do not project the PAT until the full reviewed fix is image-digest pinned and
|
||||
+ runtime authorization plus redirect credential isolation are verified.
|
||||
+ Then deliver through existing envFrom and an apps-approved controlled rollout.
|
||||
+ Image change is explicit; preserve resource bounds unless separately reviewed.
|
||||
+ Rollout must account for SQLite/PVC and single-node headroom before approval.
|
||||
+- **modify** owner-controlled runtime config `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST`
|
||||
+ to map `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` to the exact fourteen consented
|
||||
+ pinned API URL strings: seven indexes and seven capability entries. Candidate
|
||||
+ config is `builds/reuse-surface-private-source-reader/auth-source-allowlist.json`;
|
||||
+ registrations cannot set or widen this runtime authority. The patched consumer
|
||||
+ checks exact URL membership **before credential lookup**, requires HTTPS and
|
||||
+ refuses authenticated redirects including same-origin path changes. Apps/reuse
|
||||
+ must safely inventory existing authenticated registrations (auth_env and URL
|
||||
+ metadata only), review which destinations remain legitimate, and migrate their
|
||||
+ mappings before fail-closed rollout. Inventory is not blanket consent. The new
|
||||
+ reader mapping stays exact14; any existing legitimate other mapping needs its
|
||||
+ own owner-reviewed authorization. Missing/invalid mappings fail closed.
|
||||
+- **create** proposed ops-warden pointer `reuse-surface-forgejo-source-read` in
|
||||
+ draft status after construction, owner `railiance-platform`, non-SSH route
|
||||
+ only (`warden_executes:false`). Runtime reader is the hub, not warden.
|
||||
+ Activation remains ops-warden's independent verified catalog review.
|
||||
+
|
||||
+### Full consumer candidate pins
|
||||
+
|
||||
+Reuse owner provided full patch SHA256
|
||||
+`f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e`,
|
||||
+fixed `federation.py` SHA256
|
||||
+`dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd`,
|
||||
+native verification helper `private_source_verify.py` SHA256
|
||||
+`c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c`,
|
||||
+and owner runtime allowlist SHA256
|
||||
+`e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286`.
|
||||
+The ops-mason bundled compact allowlist has the same exact env and fourteen URL
|
||||
+sets; serialization/order differs. The construction inputs retain absolute
|
||||
+isolated-owner artifact references for integration. Owner reports focused30pass
|
||||
+and whole226pass with one isolated-relative-path failure; this is not native
|
||||
+runtime proof. Fixed image digest and owner execution review remain pending.
|
||||
+
|
||||
+### Revision preservation
|
||||
+
|
||||
+R2 refines the same proposal and trial; R1 is preserved under custodian
|
||||
+`owner-assembly/mason-r1/` and `/tmp/cust81-mason-reader-r1-snapshot`. R1 candidate
|
||||
+spec digest was `0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980`.
|
||||
+Neither revision was approved or executed. The full consumer fix and runtime
|
||||
+allowlist supersede the earlier redirect-only image gate. Approval, when sought,
|
||||
+must bind this final reviewed revision, its consumer patch, runtime config and
|
||||
+image; do not create another scored trial from this refinement.
|
||||
+
|
||||
+### Residual-secret lifecycle and elimination
|
||||
+
|
||||
+Credential owner is railiance-platform; Forgejo owns target grants; reuse-surface
|
||||
+is the only workload consumer. Authority exposes **entire source code of the
|
||||
+seven repositories**, not just registry files. Metadata disclosure approval
|
||||
+alone does not authorize this larger repository-read grant.
|
||||
+
|
||||
+Known intended copies: OpenBao KV current version and retained versions/backups;
|
||||
+Kubernetes Secret in `reuse` and etcd backups; hub process environment; transient
|
||||
+minting/delivery memory. Every namespace actor allowed to attach the ESO store or
|
||||
+reference the materialized Secret is an indirect consumer/exposure path and must
|
||||
+be reviewed; namespace conditions are not per-pod isolation. No CI/chat/local
|
||||
+credential file copy is allowed. Avoid Secret last-applied annotation copies.
|
||||
+
|
||||
+Proposed lifetime policy is attended rotation **every 90 days**, and immediately
|
||||
+on compromise, owner change, consumer retirement or scope drift. This is a
|
||||
+recommended stricter policy than CCR-0031's 180-day precedent; it is not a claim
|
||||
+of automated enforcement or PAT native expiry. Record issuance and next-due UTC
|
||||
+in platform inventory, with an assigned owner reminder before activation.
|
||||
+Rotation: mint one replacement with the same checked grant; compare scopes and
|
||||
+all effective grants; write it as a new KV version without changing other fields;
|
||||
+wait for ESO, roll the reviewed fixed image, verify all seven native fetches, then revoke old
|
||||
+PAT by recorded ID. Replacement overlap must end within 24 hours. Failed or
|
||||
+ambiguous replacement issuance is reconciled by token-name/ID inventory before
|
||||
+retry; no blind minting. Review retained KV versions/backups under platform's
|
||||
+retention policy; obsolete token copies are harmless only after target revocation.
|
||||
+
|
||||
+Immediate revocation deletes the dedicated PAT by ID, then removes/disables only
|
||||
+this identity's team grants as approved. Compromise response reviews Forgejo
|
||||
+access/audit metadata and scans owned repositories and approved logs for leaks
|
||||
+without placing tokens in prompts. Use secret scanners/redactors and suppressed
|
||||
+negative probes. Availability strategy fails private refresh closed and marks
|
||||
+consented cache stale; public discovery continues. Never substitute privileged
|
||||
+personal/admin/core-hub credentials during issuer outage.
|
||||
+
|
||||
+Elimination depends on Forgejo/native federation or an operationally viable
|
||||
+short-lived service credential, plus native consumer support. Revisit at rotation
|
||||
+and at target upgrades; retain a live owner record for that engineering debt.
|
||||
+
|
||||
+## 4. Review notes (ops-mason phase 2, incomplete)
|
||||
+
|
||||
+Reuse existing delivery identity and stores rather than introduce AppRole/static
|
||||
+ESO authority. The new PAT is isolated; caller writer policy stays byte-identical.
|
||||
+The proposed full ESO policy preserves every original byte before one added exact
|
||||
+read stanza; integration must compare current live and source digests. If policy
|
||||
+changed, rebase, review and pin a fresh digest rather than overwrite it.
|
||||
+
|
||||
+Existing `ops_mason.executor` engines do not express Forgejo user/team/PAT and
|
||||
+shared-policy extension. Do not mislabel this JSON as a supported build_spec or
|
||||
+pass it to a single-KV builder: that could replace existing policy. Candidate
|
||||
+spec digest is SHA256 of UTF-8 sorted compact JSON with allow_nan=false. It
|
||||
+binds scope; it is neither approval nor an executable adapter. Platform must
|
||||
+allocate CCR, own a silent idempotent provisioner and pin all execution/artifact
|
||||
+inputs before phase 3. Approval of a construction plan does not supply a token.
|
||||
+
|
||||
+This draft is not yet phase-3 ready. Gaps: scoped live structure survey, target
|
||||
+Q1–Q3 confirmation, effective-access enumeration design, reviewed platform CCR
|
||||
+and script, consumer raw-route/Bearer behavior, full authorization/redirect-fixed image and runtime allowlist proof plan, apps exact deployment
|
||||
+and headroom/PVC rollback plan, independent owner reviews. No owner review is
|
||||
+written on another repo's behalf. Only ops-mason's own preparation findings are
|
||||
+recorded here. No own autopilot grant or monetary risk limit is claimed.
|
||||
+
|
||||
+## 5. Executive summary (candidate; not yet ready for approval)
|
||||
+
|
||||
+1. **One-line ask.** Give the hosted capability hub a dedicated read-only account
|
||||
+ for the seven private repositories whose capability metadata you consented
|
||||
+ to publish, delivered using the existing workload-identity path.
|
||||
+2. **Who/what gets access.** Forgejo user `reuse-surface-source` obtains the
|
||||
+ seven-repository read grant; the existing `reuse-surface-runtime-eso` identity
|
||||
+ reads one additional exact custody entry for the hub. Namespace actors able
|
||||
+ to reuse the delivery store/Secret remain an exposure consideration to review.
|
||||
+3. **To what.** Read-only source access across the seven named private repositories,
|
||||
+ with no write/admin/non-code-unit grants. The hub publishes only the pinned
|
||||
+ consented capability metadata; repository visibility stays private.
|
||||
+4. **For how long.** Bao delivery tokens remain 15 minutes, but the Forgejo PAT
|
||||
+ has no expiry demonstrated. Proposed attended rotation is every 90 days with
|
||||
+ immediate revocation on compromise; this distinction must be accepted explicitly.
|
||||
+5. **Blast radius if the credential leaks.** Entire private source of those seven
|
||||
+ repositories can be read. The token must have no additional private-repository
|
||||
+ access and cannot write. Metadata disclosure consent is narrower than that read
|
||||
+ grant. Availability impact of controlled rollout remains owner-reviewed.
|
||||
+6. **Cost to reverse.** Revoke one PAT, remove only its dedicated grants, remove
|
||||
+ one new ESO data item and one new policy stanza, and roll the reviewed fixed image.
|
||||
+ Reconcile private registrations from the exact attempt; preserve the existing
|
||||
+ public capabilities and writer/webhook keys. No additional paid infrastructure
|
||||
+ is proposed (incremental estimate EUR 0); labor and credible loss remain unpriced,
|
||||
+ so this stays supervised.
|
||||
+
|
||||
+After the missing reviews and executable inputs are attached, the founder can
|
||||
+**approve**, **reject**, or **send back to phase 1**. No founder disposition is
|
||||
+recorded for this credential proposal. Existing metadata consent is retained as
|
||||
+separate evidence and does not set status approved.
|
||||
+
|
||||
+## 6. Proposed verification and rollback contract
|
||||
+
|
||||
+This section describes future acceptance, **not a build result**. Phase 4 has not
|
||||
+run and no success evidence or active catalog entry exists.
|
||||
+
|
||||
+Before changes: exact CCR/plan/script/manifest digests, owner reviews and founder
|
||||
+approved revision must match; metadata survey proves current path/policy/role;
|
||||
+source/consent pins match; all existing user/team conflicts resolved; previous
|
||||
+policy and deployment revision retained; apps runs dry-run and client diff,
|
||||
+confirms target node and headroom/PVC strategy. Never read or print Secret objects.
|
||||
+
|
||||
+Before projecting any PAT into the hub: reuse owner patch must close native
|
||||
+both redirect Authorization forwarding and registration-controlled auth_env/URL
|
||||
+exfiltration. Tests must prove unlisted URL refusal before credential lookup and
|
||||
+no redirect credential forwarding. Apps must pin the full fixed image and exact
|
||||
+runtime allowlist configuration; review/migrate existing authenticated sources
|
||||
+before rollout. The earlier same-image and redirect-only assumptions are
|
||||
+superseded; full fixed image/source/patch and owner-runtime config approval remain
|
||||
+pending. The candidate allowlist encodes the consent packet exact fourteen URLs.
|
||||
+
|
||||
+After target issuance, scope metadata must be exactly read:repository, team repo
|
||||
+set exactly seven and complete effective grants enumerated. Choose a known private
|
||||
+outside repo by target-owner metadata and prove denial without body retention.
|
||||
+Use safe read-only negative admin/non-code routes; never try writes as a negative
|
||||
+probe because unexpected privilege could make them succeed. Verify absence of
|
||||
+write authority through scope and effective permissions. Missing auth/anonymous
|
||||
+private raw reads fail closed, never cached as fresh successes.
|
||||
+
|
||||
+Bao positive receipt proves ESO role exact path read; negative receipts prove
|
||||
+default identity and dedicated caller writer cannot read the new path, ESO cannot
|
||||
+read an unrelated sibling, and wrong SA/namespace/audience cannot authenticate.
|
||||
+These are effective token-specific checks, not a test on an admin session.
|
||||
+Owner suppressing helper checks ESO Ready and materialized key presence only.
|
||||
+
|
||||
+In the actual running hub, native `fetch_remote_index_text` must fetch all seven
|
||||
+pinned URLs using `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` and native header code;
|
||||
+validate schema, exact index hashes, exact expected IDs and relative entry handling
|
||||
+without printing private bodies. Local curl success is insufficient for native
|
||||
+runtime verification. Then register seven pinned sources with the approved writer
|
||||
+lane and force fresh composition; prove all 70 reviewed IDs exactly once, no stale
|
||||
+private fallback and matching metadata. Public URLs/entries and source bytes must
|
||||
+stay inside disclosure consent; new commits need renewed metadata review.
|
||||
+
|
||||
+Rollback order: immediately revoke dedicated PAT; disable only new private
|
||||
+registrations after reconciling ambiguous requests; remove/revert exact new
|
||||
+ESO data item and policy stanza through owner paths; remove only the new
|
||||
+materialized token key with a suppressing helper, preserve writer/webhook keys;
|
||||
+controlled approved-image rollout. A previously vulnerable image must never
|
||||
+receive the new reader key; verify key removal before restoring such an image.
|
||||
+Restore previous pinned policy/deployment revision
|
||||
+when applicable. Retain partial, failed and unverified outcomes and token IDs.
|
||||
+Do not delete the shared Secret/store/role, overwrite unrelated policy changes,
|
||||
+or remove all public registrations. Restore only the new reader runtime mapping,
|
||||
+preserving separately reviewed existing authenticated-source mappings. No transactional rollback is claimed.
|
||||
diff --git a/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md b/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md
|
||||
new file mode 100644
|
||||
index 0000000..4d6d383
|
||||
--- /dev/null
|
||||
+++ b/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md
|
||||
@@ -0,0 +1,80 @@
|
||||
+---
|
||||
+id: MASON-WP-0013
|
||||
+type: workplan
|
||||
+title: "Construct the seven-repository reuse-surface source reader"
|
||||
+domain: infotech
|
||||
+repo: ops-mason
|
||||
+status: proposed
|
||||
+owner: codex
|
||||
+topic_slug: netkingdom-security-architecture
|
||||
+created: "2026-10-15"
|
||||
+updated: "2026-10-15"
|
||||
+flavor: implementation
|
||||
+depends_on: []
|
||||
+---
|
||||
+
|
||||
+# Seven-repository hosted reader
|
||||
+
|
||||
+Demand: CUST-WP-0081; metadata disclosure consent is recorded separately. This
|
||||
+workplan is prepared in an isolated owner checkout and is not yet integrated,
|
||||
+synced, approved or executed. No production or credential authority is inferred.
|
||||
+
|
||||
+## Prepare the construction bundle
|
||||
+
|
||||
+```task
|
||||
+id: MASON-WP-0013-T01
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Prepared `plans/reuse-surface-private-source-reader.md` and
|
||||
+`builds/reuse-surface-private-source-reader/` with exact-seven scope, candidate
|
||||
+ESO policy, data item, canonical candidate specification digest and SEC-DSM
|
||||
+hierarchy/lifecycle analysis. R2 refines the same proposal: full consumer
|
||||
+auth_env/URL authorization and redirect fix plus exact14 runtime allowlist gate,
|
||||
+with R1 evidence preserved. Plan remains draft because required live survey
|
||||
+and independent mechanism/owner reviews are missing.
|
||||
+
|
||||
+## Complete scoped survey and construction review
|
||||
+
|
||||
+```task
|
||||
+id: MASON-WP-0013-T02
|
||||
+status: wait
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Requires legitimate own metadata-only survey grant or sanitized platform-owned
|
||||
+survey. Confirm current exact path/policy/role and matching-existing identity
|
||||
+handling; Forgejo owner confirms Q1–Q3 hierarchy choices. Review effective grants
|
||||
+across all API pages, secret lifecycle, negative tests and caller/ESO separation.
|
||||
+Do not use operator tokens or another agent supervision record.
|
||||
+
|
||||
+## Bind owner CCR and executable inputs to founder decision
|
||||
+
|
||||
+```task
|
||||
+id: MASON-WP-0013-T03
|
||||
+status: wait
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Platform allocates CCR and reviewed silent adapter; apps owns concrete rollout
|
||||
+and rollback artifacts including full fixed image, runtime-controlled allowlist
|
||||
+and reviewed migration of existing authenticated sources; reuse owns native fetch/public metadata validation;
|
||||
+warden owns pointer review. Obtain separate founder decision on exact credential
|
||||
+and live-change revision/digests. Existing disclosure consent cannot approve
|
||||
+repository-wide credential grant. Requires T02.
|
||||
+
|
||||
+## Verify the authorized owner build and routing handoff
|
||||
+
|
||||
+```task
|
||||
+id: MASON-WP-0013-T04
|
||||
+status: wait
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+After T03, founder/platform privileged path executes, ops-mason observes only
|
||||
+through scoped permitted metadata. Preserve outcome/rollback receipts, complete
|
||||
+native seven-index positive and least-privilege negative checks, propose draft
|
||||
+catalog route and hand 70-ID fresh audit evidence to CUST-WP-0081. Never publish
|
||||
+success before actual receipt or finish while unresolved lifecycle work is only
|
||||
+in prose.
|
||||
diff --git a/builds/reuse-surface-private-source-reader/auth-source-allowlist.json b/builds/reuse-surface-private-source-reader/auth-source-allowlist.json
|
||||
new file mode 100644
|
||||
index 0000000..e615527
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/auth-source-allowlist.json
|
||||
@@ -0,0 +1 @@
|
||||
+{"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN":["https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"]}
|
||||
diff --git a/builds/reuse-surface-private-source-reader/candidate-spec.sha256 b/builds/reuse-surface-private-source-reader/candidate-spec.sha256
|
||||
new file mode 100644
|
||||
index 0000000..d60e48d
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/candidate-spec.sha256
|
||||
@@ -0,0 +1 @@
|
||||
+59a7334eb58135790547fab0a8c8e8debb94cc7acaae88d30654c9ae887d6732 canonical-sorted-compact-json(construction-inputs.json)
|
||||
diff --git a/builds/reuse-surface-private-source-reader/construction-inputs.json b/builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
new file mode 100644
|
||||
index 0000000..47311da
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
@@ -0,0 +1,278 @@
|
||||
+{
|
||||
+ "id": "MASON-CUST81-PRIVATE-READER-R1",
|
||||
+ "revision": 2,
|
||||
+ "status": "draft-not-executable",
|
||||
+ "engine": "platform-owned-forgejo-restricted-reader-and-existing-eso-delta",
|
||||
+ "execution_support": "requires platform reviewed adapter; not an ops_mason.executor supported build_spec",
|
||||
+ "preparation_sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99",
|
||||
+ "metadata_consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
+ "disclosure_packet_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35",
|
||||
+ "identity": {
|
||||
+ "forgejo_user": "reuse-surface-source",
|
||||
+ "forgejo_org": "coulomb",
|
||||
+ "forgejo_team": "reuse-surface-source-readers",
|
||||
+ "restricted": true,
|
||||
+ "is_admin": false,
|
||||
+ "visibility": "private",
|
||||
+ "password": "random value retained only in attended process memory; never stored in evidence",
|
||||
+ "team_permission": "read",
|
||||
+ "includes_all_repositories": false,
|
||||
+ "can_create_org_repo": false,
|
||||
+ "units_map": {
|
||||
+ "repo.code": "read",
|
||||
+ "repo.actions": "none",
|
||||
+ "repo.packages": "none",
|
||||
+ "repo.issues": "none",
|
||||
+ "repo.ext_issues": "none",
|
||||
+ "repo.wiki": "none",
|
||||
+ "repo.pulls": "none",
|
||||
+ "repo.releases": "none",
|
||||
+ "repo.projects": "none",
|
||||
+ "repo.ext_wiki": "none"
|
||||
+ },
|
||||
+ "team_repositories": [
|
||||
+ "coulomb/rapp-openbao",
|
||||
+ "coulomb/rapp-policy-nexus",
|
||||
+ "coulomb/rapp-postgres",
|
||||
+ "coulomb/rapp-sbom-nexus",
|
||||
+ "coulomb/rapp-telemetry",
|
||||
+ "coulomb/rapp-tenant-engine",
|
||||
+ "coulomb/rapp-user-engine"
|
||||
+ ],
|
||||
+ "token_scopes": [
|
||||
+ "read:repository"
|
||||
+ ],
|
||||
+ "token_name_prefix": "reuse-surface-source-read-",
|
||||
+ "reuse_other_workload_token": false,
|
||||
+ "fail_on_differing_existing_identity_or_team": true,
|
||||
+ "enforcement": "Verify complete team membership and all effective user repository grants; no other team, collaborator or organisation grants may provide extra private read access."
|
||||
+ },
|
||||
+ "delivery": {
|
||||
+ "kv_mount": "platform",
|
||||
+ "kv_path": "platform/workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
+ "field": "FORGEJO_SOURCE_TOKEN",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
+ "eso_role": "reuse-surface-runtime-eso",
|
||||
+ "eso_identity": "reuse/reuse-surface-eso",
|
||||
+ "audience": "openbao",
|
||||
+ "ttl": "15m",
|
||||
+ "cluster_secret_store": "openbao-reuse",
|
||||
+ "store_namespace_condition": [
|
||||
+ "reuse"
|
||||
+ ],
|
||||
+ "external_secret": "reuse/reuse-surface-runtime",
|
||||
+ "target_secret": "reuse/reuse-surface-env",
|
||||
+ "existing_consumption": "Existing envFrom interface is reusable only after approved full fixed image plus runtime-owned exact14 URL/auth-env allowlist is active. Existing authenticated sources need separately reviewed safe inventory and migration before fail-closed rollout.",
|
||||
+ "policy_delta": {
|
||||
+ "file": "railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
+ "add_exact_stanza": "path \"platform/data/workloads/reuse/reuse-surface/forgejo-source-read\" { capabilities = [\"read\"] }",
|
||||
+ "retain_existing_paths": true,
|
||||
+ "do_not_change": "Caller-facing workload-kv-read-reuse-surface-runtime policy or writer OIDC role; those remain unable to read this new path."
|
||||
+ },
|
||||
+ "external_secret_data_delta": {
|
||||
+ "secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
+ "remoteRef": {
|
||||
+ "key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
+ "property": "FORGEJO_SOURCE_TOKEN"
|
||||
+ }
|
||||
+ },
|
||||
+ "delivery_design_reason": "Uses the existing namespace-limited ESO store and identity plus the existing envFrom interface. Separate new KV path keeps scoped writer callers from also acquiring private-source access. No new static ESO token or new store is needed.",
|
||||
+ "native_source_auth": {
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
+ "auth_header": "Authorization",
|
||||
+ "header_behavior": "Native federation _auth_headers prefixes a bare token with Bearer; owner must verify accepted Forgejo raw API route with this actual native implementation."
|
||||
+ },
|
||||
+ "fixed_image_gate": {
|
||||
+ "status": "pending owner patch and reviewed image",
|
||||
+ "reason": "Current native fetch permits registration-selected auth_env plus arbitrary URL and forwards Authorization on redirect. Both must be fixed before credential delivery.",
|
||||
+ "required": [
|
||||
+ "reuse owner full redirect and auth_env/URL patch and meaningful boundary tests",
|
||||
+ "exact full source/patch digest",
|
||||
+ "approved image digest built from fixed source",
|
||||
+ "runtime controlled exact14 auth allowlist config digest",
|
||||
+ "safe owner inventory and reviewed migration of existing authenticated sources",
|
||||
+ "runtime proof denied URLs are checked before credential lookup and authenticated redirects never forward credential",
|
||||
+ "apps reviewed rollout and rollback with image/config changes explicit"
|
||||
+ ],
|
||||
+ "current_image_accepts_new_pat": false,
|
||||
+ "reviewed_candidate_inputs": {
|
||||
+ "consumer_owner_allowlist_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json",
|
||||
+ "consumer_owner_allowlist_sha256": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
+ "consumer_patch_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/reuse-owner-preparation-r1.patch",
|
||||
+ "consumer_patch_sha256": "f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e",
|
||||
+ "fixed_federation_source_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
+ "allowlist_semantic_crosscheck": "pass exact same env and exact14 URL sets; serialization ordering differs",
|
||||
+ "image_digest": null,
|
||||
+ "owner_review_status": "baseline changes_requested; full candidate must integrate and obtain runtime proof",
|
||||
+ "test_report": "owner reports focused30pass; whole226pass1isolatedrelativepath failure; no live runtime proof",
|
||||
+ "native_verification_helper_sha256": "c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c",
|
||||
+ "native_verification_helper_artifact": "/tmp/cust81-reuse-reader-owner/reuse_surface/private_source_verify.py"
|
||||
+ }
|
||||
+ },
|
||||
+ "runtime_auth_source_allowlist": {
|
||||
+ "env": "REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST",
|
||||
+ "candidate_value": "{\"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\":[\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\"]}",
|
||||
+ "candidate_artifact": "builds/reuse-surface-private-source-reader/auth-source-allowlist.json",
|
||||
+ "artifact_sha256": "9a5d5a1ffb6ff1fa9e0cba7fb77779fd832c5bff413718d63c6f6e314ac653fc",
|
||||
+ "semantic_value_sha256": "30c823e0ccd4cd5bcccacfa731bcf0674b63ba6d5ec79f2c012ec650201f81f5",
|
||||
+ "policy": "Only runtime owner may configure; untrusted registration cannot grant itself authorization. Exact equality checked before any credential env lookup. Exact14 pinned index and entry API URLs only.",
|
||||
+ "existing_authenticated_sources": "Owner must safely inventory all authenticated sources using sanitized auth_env/URL metadata and migrate valid existing mappings before fail-closed rollout; do not expose secrets or blindly authorize inventory."
|
||||
+ }
|
||||
+ },
|
||||
+ "sources": [
|
||||
+ {
|
||||
+ "repo": "rapp-openbao",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.openbao-package"
|
||||
+ ],
|
||||
+ "consented_commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
+ "index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-policy-nexus",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.policy-publication-package"
|
||||
+ ],
|
||||
+ "consented_commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
+ "index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-postgres",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.postgres-isolation-recovery"
|
||||
+ ],
|
||||
+ "consented_commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
+ "index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-sbom-nexus",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.sbom-runtime-package"
|
||||
+ ],
|
||||
+ "consented_commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
+ "index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-telemetry",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.telemetry-package"
|
||||
+ ],
|
||||
+ "consented_commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
+ "index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-tenant-engine",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.tenant-engine-package"
|
||||
+ ],
|
||||
+ "consented_commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
+ "index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ },
|
||||
+ {
|
||||
+ "repo": "rapp-user-engine",
|
||||
+ "capability_ids": [
|
||||
+ "capability.railiance.user-engine-package"
|
||||
+ ],
|
||||
+ "consented_commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
+ "native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
+ "index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
+ "auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
+ }
|
||||
+ ],
|
||||
+ "policy_before_sha256": "fab29572fe82fc6674837e430f616409070f97a3867a411b7e21b5141dfaba20",
|
||||
+ "policy_candidate_sha256": "788bb9c49ab571e522beeecf7a5e29d1c79c842a84fe6d801978655facd3b5bd",
|
||||
+ "caller_writer_policy_sha256": "53dd5eecc94fd8d7fc281a858f5d9fc27c8c895fd79161fd113cd3a1387a1257",
|
||||
+ "lifecycle": {
|
||||
+ "owner": "railiance-platform",
|
||||
+ "target_owner": "railiance-forge",
|
||||
+ "consumer": "reuse-surface hosted process only",
|
||||
+ "rotation_days": 90,
|
||||
+ "expiry": "No PAT native expiry demonstrated; 90-day attended rotation is proposed owner policy, not implemented expiry enforcement.",
|
||||
+ "automatic_rotation": false,
|
||||
+ "revoke_on": [
|
||||
+ "compromise",
|
||||
+ "scope drift",
|
||||
+ "owner or consumer change",
|
||||
+ "90-day rotation after replacement verifies"
|
||||
+ ],
|
||||
+ "replacement_overlap_max_hours": 24,
|
||||
+ "outage": "Private refresh fails closed; prior consented cache visibly stale, public sources continue. Never substitute admin/personal/core-hub PAT."
|
||||
+ },
|
||||
+ "approval": {
|
||||
+ "supervisor": "Bernd Worsch, GOVERN @ estate",
|
||||
+ "approved_by": null,
|
||||
+ "approved_at": null,
|
||||
+ "approved_digest": null,
|
||||
+ "metadata_disclosure_is_not_credential_approval": true
|
||||
+ },
|
||||
+ "pre_execution_gates": [
|
||||
+ "own scoped metadata survey or platform sanitized equivalent",
|
||||
+ "Forgejo owner Q1-Q3 mechanism confirmation",
|
||||
+ "platform CCR allocation and reviewed silent provisioner",
|
||||
+ "effective exact-seven authorization proof design",
|
||||
+ "own owner reviews including reuse native fetch and apps rollout",
|
||||
+ "founder exact revision approval",
|
||||
+ "headroom/PVC/rollout preflight",
|
||||
+ "reuse owner redirect patch reviewed and fixed image approved",
|
||||
+ "verify fixed image before projecting reader PAT into hub process",
|
||||
+ "full consumer source-authorization patch reviewed, not redirect-only",
|
||||
+ "runtime owner exact14 allowlist and existing auth-source migration reviewed"
|
||||
+ ],
|
||||
+ "verification": {
|
||||
+ "pre_apply": [
|
||||
+ "Pin exact CCR and plan revision/digest, owner reviews, founder disposition and rollout artifacts. Resolve existing identity/team/path conflicts before any mutation.",
|
||||
+ "Confirm live namespace/Deployment and its image digest, current ESO identity/binding and secret field presence through safe suppressing helper; no Secret dump.",
|
||||
+ "Check CPU headroom and rollout strategy/PVC constraints; retain exact prior source manifest/image and server dry-run/client diff.",
|
||||
+ "Current native fetch redirect forwarding hazard must be fixed in the reviewed image before PAT delivery to the hub. An unchanged vulnerable image is not acceptable.",
|
||||
+ "Full consumer patch must reject registration-controlled auth_env/URL destinations before credential lookup; configure owner-reviewed allowlist and migrate existing authenticated sources before rollout."
|
||||
+ ],
|
||||
+ "positive": [
|
||||
+ "PAT metadata scope exactly read:repository; exact seven team repos with no other private grants.",
|
||||
+ "Actual native fetch_remote_index_text from hub process with the approved auth_env fetches all seven pinned raw index URLs; validate schema, one expected capability each and exact index SHA256. No token or fetched private source output.",
|
||||
+ "ESO Ready and materialized key present checked without values; Deployment Ready on reviewed fixed-image digest after controlled rollout; redirect credential isolation verified.",
|
||||
+ "Register private sources only after credential and native fetch receipts; fresh hosted federation then proves all70 reviewed IDs exactly once and matching metadata."
|
||||
+ ],
|
||||
+ "negative": [
|
||||
+ "Reader cannot fetch a known private repository outside the seven (preselected by platform using metadata, no unauthorized body retention).",
|
||||
+ "Forgejo writes, admin/user operations and non-code unit access denied; probe design must avoid creating writes if an unexpected permission exists.",
|
||||
+ "Default identity and scoped writer OIDC caller cannot read new KV path; ESO role cannot read any other sibling path, and wrong SA/namespace/audience cannot authenticate.",
|
||||
+ "Native missing-auth and anonymous private-index fetch fail closed; no fallback to admin/personal/core-hub credentials.",
|
||||
+ "Registration-selected auth_env or unlisted same-host/other-host/unpinned/wrong-query URL is refused before credential lookup; token value is never read or transmitted.",
|
||||
+ "Authenticated redirects fail closed without forwarding Authorization, including same-origin path changes. Runtime allowlist contains exact14 consented pinned URL strings only for new readerenv."
|
||||
+ ],
|
||||
+ "receipts": "Sanitized IDs, repo names, scopes, hashes, status booleans and timestamps only; no credentials, raw errors, private index bodies or Secret objects."
|
||||
+ },
|
||||
+ "rollback": {
|
||||
+ "immediate": "Revoke this dedicated PAT and remove exact new team/user grants. Disable only private registrations created by the associated attempt after reconciling ambiguous requests; preserve all public registrations.",
|
||||
+ "delivery": "Remove only new ES data item and exact new ESO policy stanza, restore prior source revision through owner path, delete only new materialized token key if needed using suppressed owner helper, then roll the reviewed fixed image or restore a prior image only after credential removal is verified. Do not delete reuse-surface-env or rotate existing writer/webhook fields. Remove only the new reader environment mapping from runtime source allowlist while preserving separately reviewed existing mappings; coordinate image/config rollback so stale deployments never receive the PAT.",
|
||||
+ "preserve_evidence": "Retain failed/partial outcomes, reconcile any ambiguous identity or token creation; never blindly remint after an observation timeout."
|
||||
+ },
|
||||
+ "cost_estimate_eur": 0,
|
||||
+ "cost_assumptions": "Incremental cloud infrastructure only; existing service/resources. Build and rollout of reviewed fixed image is required. Human labor/compute changes not priced; re-estimate if owner rollout changes resources.",
|
||||
+ "credible_loss_estimate_eur": null,
|
||||
+ "risk_unpriced_reason": "Repository-wide private source exposure and availability impact cannot be bounded from local evidence; supervised execution required.",
|
||||
+ "revision_history": [
|
||||
+ {
|
||||
+ "revision": 1,
|
||||
+ "spec_sha256": "0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980",
|
||||
+ "status": "draft-unapproved-never-executed",
|
||||
+ "ref": "custodian owner-assembly/mason-r1/"
|
||||
+ },
|
||||
+ {
|
||||
+ "revision": 2,
|
||||
+ "reason": "Same proposal refined after consumer owner found URL/auth_env credential exfiltration in addition to redirect forwarding."
|
||||
+ }
|
||||
+ ]
|
||||
+}
|
||||
diff --git a/builds/reuse-surface-private-source-reader/externalsecret-data-item.json b/builds/reuse-surface-private-source-reader/externalsecret-data-item.json
|
||||
new file mode 100644
|
||||
index 0000000..bbfa8ab
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/externalsecret-data-item.json
|
||||
@@ -0,0 +1,7 @@
|
||||
+{
|
||||
+ "secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
+ "remoteRef": {
|
||||
+ "key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
+ "property": "FORGEJO_SOURCE_TOKEN"
|
||||
+ }
|
||||
+}
|
||||
diff --git a/builds/reuse-surface-private-source-reader/workload-kv-read-reuse-surface-runtime-eso.hcl b/builds/reuse-surface-private-source-reader/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
new file mode 100644
|
||||
index 0000000..a878399
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
@@ -0,0 +1,15 @@
|
||||
+path "platform/data/workloads/reuse/reuse-surface/runtime-secrets" {
|
||||
+ capabilities = ["read"]
|
||||
+}
|
||||
+
|
||||
+path "auth/token/lookup-self" {
|
||||
+ capabilities = ["read"]
|
||||
+}
|
||||
+
|
||||
+path "auth/token/revoke-self" {
|
||||
+ capabilities = ["update"]
|
||||
+}
|
||||
+
|
||||
+path "platform/data/workloads/reuse/reuse-surface/forgejo-source-read" {
|
||||
+ capabilities = ["read"]
|
||||
+}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
diff --git a/tmp/cust81-mason-reader-r1-snapshot/plans/reuse-surface-private-source-reader.md b/plans/reuse-surface-private-source-reader.md
|
||||
index 5d9770d..0ac705b 100644
|
||||
--- a/tmp/cust81-mason-reader-r1-snapshot/plans/reuse-surface-private-source-reader.md
|
||||
+++ b/plans/reuse-surface-private-source-reader.md
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
id: reuse-surface-private-source-reader
|
||||
+revision: 2
|
||||
+proposal_id: MASON-CUST81-PRIVATE-READER-R1
|
||||
demand_source: the-custodian/workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md
|
||||
consumer_repo: reuse-surface
|
||||
credential_type: forgejo-restricted-source-read-with-existing-kubernetes-eso
|
||||
@@ -9,7 +11,7 @@ approved_at: null
|
||||
created: "2026-10-15"
|
||||
updated: "2026-10-15"
|
||||
construction_inputs: builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
-candidate_spec_sha256: 0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980
|
||||
+candidate_spec_sha256: 59a7334eb58135790547fab0a8c8e8debb94cc7acaae88d30654c9ae887d6732
|
||||
approved_spec_sha256: null
|
||||
filed_as: null
|
||||
---
|
||||
@@ -156,17 +158,56 @@ Unconfirmed Q1–Q3 are explicit review gates, not asserted facts.
|
||||
`FORGEJO_SOURCE_TOKEN`. Bundled JSON item is a proposal for apps integration,
|
||||
not a standalone apply object. Preserve existing writer and webhook fields.
|
||||
- **modify** native fetch code/image through reuse and apps review: reuse owner
|
||||
- identified Authorization header forwarding on redirects in the current urllib
|
||||
- implementation. Do not project the PAT into the running hub until a reviewed
|
||||
- fixed image is digest-pinned and runtime redirect credential isolation verified.
|
||||
+ identified both Authorization header forwarding on redirects and registration-
|
||||
+ controlled auth_env/URL credential exfiltration in the current native fetch.
|
||||
+ Do not project the PAT until the full reviewed fix is image-digest pinned and
|
||||
+ runtime authorization plus redirect credential isolation are verified.
|
||||
Then deliver through existing envFrom and an apps-approved controlled rollout.
|
||||
Image change is explicit; preserve resource bounds unless separately reviewed.
|
||||
Rollout must account for SQLite/PVC and single-node headroom before approval.
|
||||
+- **modify** owner-controlled runtime config `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST`
|
||||
+ to map `REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN` to the exact fourteen consented
|
||||
+ pinned API URL strings: seven indexes and seven capability entries. Candidate
|
||||
+ config is `builds/reuse-surface-private-source-reader/auth-source-allowlist.json`;
|
||||
+ registrations cannot set or widen this runtime authority. The patched consumer
|
||||
+ checks exact URL membership **before credential lookup**, requires HTTPS and
|
||||
+ refuses authenticated redirects including same-origin path changes. Apps/reuse
|
||||
+ must safely inventory existing authenticated registrations (auth_env and URL
|
||||
+ metadata only), review which destinations remain legitimate, and migrate their
|
||||
+ mappings before fail-closed rollout. Inventory is not blanket consent. The new
|
||||
+ reader mapping stays exact14; any existing legitimate other mapping needs its
|
||||
+ own owner-reviewed authorization. Missing/invalid mappings fail closed.
|
||||
- **create** proposed ops-warden pointer `reuse-surface-forgejo-source-read` in
|
||||
draft status after construction, owner `railiance-platform`, non-SSH route
|
||||
only (`warden_executes:false`). Runtime reader is the hub, not warden.
|
||||
Activation remains ops-warden's independent verified catalog review.
|
||||
|
||||
+### Full consumer candidate pins
|
||||
+
|
||||
+Reuse owner provided full patch SHA256
|
||||
+`f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e`,
|
||||
+fixed `federation.py` SHA256
|
||||
+`dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd`,
|
||||
+native verification helper `private_source_verify.py` SHA256
|
||||
+`c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c`,
|
||||
+and owner runtime allowlist SHA256
|
||||
+`e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286`.
|
||||
+The ops-mason bundled compact allowlist has the same exact env and fourteen URL
|
||||
+sets; serialization/order differs. The construction inputs retain absolute
|
||||
+isolated-owner artifact references for integration. Owner reports focused30pass
|
||||
+and whole226pass with one isolated-relative-path failure; this is not native
|
||||
+runtime proof. Fixed image digest and owner execution review remain pending.
|
||||
+
|
||||
+### Revision preservation
|
||||
+
|
||||
+R2 refines the same proposal and trial; R1 is preserved under custodian
|
||||
+`owner-assembly/mason-r1/` and `/tmp/cust81-mason-reader-r1-snapshot`. R1 candidate
|
||||
+spec digest was `0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980`.
|
||||
+Neither revision was approved or executed. The full consumer fix and runtime
|
||||
+allowlist supersede the earlier redirect-only image gate. Approval, when sought,
|
||||
+must bind this final reviewed revision, its consumer patch, runtime config and
|
||||
+image; do not create another scored trial from this refinement.
|
||||
+
|
||||
### Residual-secret lifecycle and elimination
|
||||
|
||||
Credential owner is railiance-platform; Forgejo owns target grants; reuse-surface
|
||||
@@ -224,7 +265,7 @@ inputs before phase 3. Approval of a construction plan does not supply a token.
|
||||
|
||||
This draft is not yet phase-3 ready. Gaps: scoped live structure survey, target
|
||||
Q1–Q3 confirmation, effective-access enumeration design, reviewed platform CCR
|
||||
-and script, consumer raw-route/Bearer behavior and redirect-fixed image proof plan, apps exact deployment
|
||||
+and script, consumer raw-route/Bearer behavior, full authorization/redirect-fixed image and runtime allowlist proof plan, apps exact deployment
|
||||
and headroom/PVC rollback plan, independent owner reviews. No owner review is
|
||||
written on another repo's behalf. Only ops-mason's own preparation findings are
|
||||
recorded here. No own autopilot grant or monetary risk limit is claimed.
|
||||
@@ -272,9 +313,13 @@ policy and deployment revision retained; apps runs dry-run and client diff,
|
||||
confirms target node and headroom/PVC strategy. Never read or print Secret objects.
|
||||
|
||||
Before projecting any PAT into the hub: reuse owner patch must close native
|
||||
-redirect Authorization forwarding, tests must exercise the boundary, and apps
|
||||
-must pin and verify the exact fixed image. The earlier same-image preparation
|
||||
-assumption is superseded; fixed image/source/patch digests remain pending.
|
||||
+both redirect Authorization forwarding and registration-controlled auth_env/URL
|
||||
+exfiltration. Tests must prove unlisted URL refusal before credential lookup and
|
||||
+no redirect credential forwarding. Apps must pin the full fixed image and exact
|
||||
+runtime allowlist configuration; review/migrate existing authenticated sources
|
||||
+before rollout. The earlier same-image and redirect-only assumptions are
|
||||
+superseded; full fixed image/source/patch and owner-runtime config approval remain
|
||||
+pending. The candidate allowlist encodes the consent packet exact fourteen URLs.
|
||||
|
||||
After target issuance, scope metadata must be exactly read:repository, team repo
|
||||
set exactly seven and complete effective grants enumerated. Choose a known private
|
||||
@@ -308,4 +353,5 @@ receive the new reader key; verify key removal before restoring such an image.
|
||||
Restore previous pinned policy/deployment revision
|
||||
when applicable. Retain partial, failed and unverified outcomes and token IDs.
|
||||
Do not delete the shared Secret/store/role, overwrite unrelated policy changes,
|
||||
-or remove all public registrations. No transactional rollback is claimed.
|
||||
+or remove all public registrations. Restore only the new reader runtime mapping,
|
||||
+preserving separately reviewed existing authenticated-source mappings. No transactional rollback is claimed.
|
||||
diff --git a/tmp/cust81-mason-reader-r1-snapshot/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md b/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md
|
||||
index d5120ba..4d6d383 100644
|
||||
--- a/tmp/cust81-mason-reader-r1-snapshot/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md
|
||||
+++ b/workplans/MASON-WP-0013-reuse-surface-private-source-reader.md
|
||||
@@ -30,7 +30,9 @@ priority: high
|
||||
Prepared `plans/reuse-surface-private-source-reader.md` and
|
||||
`builds/reuse-surface-private-source-reader/` with exact-seven scope, candidate
|
||||
ESO policy, data item, canonical candidate specification digest and SEC-DSM
|
||||
-hierarchy/lifecycle analysis. Plan remains draft because required live survey
|
||||
+hierarchy/lifecycle analysis. R2 refines the same proposal: full consumer
|
||||
+auth_env/URL authorization and redirect fix plus exact14 runtime allowlist gate,
|
||||
+with R1 evidence preserved. Plan remains draft because required live survey
|
||||
and independent mechanism/owner reviews are missing.
|
||||
|
||||
## Complete scoped survey and construction review
|
||||
@@ -56,7 +58,8 @@ priority: high
|
||||
```
|
||||
|
||||
Platform allocates CCR and reviewed silent adapter; apps owns concrete rollout
|
||||
-and rollback artifacts; reuse owns native fetch/public metadata validation;
|
||||
+and rollback artifacts including full fixed image, runtime-controlled allowlist
|
||||
+and reviewed migration of existing authenticated sources; reuse owns native fetch/public metadata validation;
|
||||
warden owns pointer review. Obtain separate founder decision on exact credential
|
||||
and live-change revision/digests. Existing disclosure consent cannot approve
|
||||
repository-wide credential grant. Requires T02.
|
||||
diff --git a/builds/reuse-surface-private-source-reader/auth-source-allowlist.json b/builds/reuse-surface-private-source-reader/auth-source-allowlist.json
|
||||
new file mode 100644
|
||||
index 0000000..e615527
|
||||
--- /dev/null
|
||||
+++ b/builds/reuse-surface-private-source-reader/auth-source-allowlist.json
|
||||
@@ -0,0 +1 @@
|
||||
+{"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN":["https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738","https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"]}
|
||||
diff --git a/tmp/cust81-mason-reader-r1-snapshot/builds/reuse-surface-private-source-reader/candidate-spec.sha256 b/builds/reuse-surface-private-source-reader/candidate-spec.sha256
|
||||
index 4e1cdb8..d60e48d 100644
|
||||
--- a/tmp/cust81-mason-reader-r1-snapshot/builds/reuse-surface-private-source-reader/candidate-spec.sha256
|
||||
+++ b/builds/reuse-surface-private-source-reader/candidate-spec.sha256
|
||||
@@ -1 +1 @@
|
||||
-0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980 canonical-sorted-compact-json(construction-inputs.json)
|
||||
+59a7334eb58135790547fab0a8c8e8debb94cc7acaae88d30654c9ae887d6732 canonical-sorted-compact-json(construction-inputs.json)
|
||||
diff --git a/tmp/cust81-mason-reader-r1-snapshot/builds/reuse-surface-private-source-reader/construction-inputs.json b/builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
index 6c21368..47311da 100644
|
||||
--- a/tmp/cust81-mason-reader-r1-snapshot/builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
+++ b/builds/reuse-surface-private-source-reader/construction-inputs.json
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "MASON-CUST81-PRIVATE-READER-R1",
|
||||
- "revision": 1,
|
||||
+ "revision": 2,
|
||||
"status": "draft-not-executable",
|
||||
"engine": "platform-owned-forgejo-restricted-reader-and-existing-eso-delta",
|
||||
"execution_support": "requires platform reviewed adapter; not an ops_mason.executor supported build_spec",
|
||||
@@ -62,7 +62,7 @@
|
||||
],
|
||||
"external_secret": "reuse/reuse-surface-runtime",
|
||||
"target_secret": "reuse/reuse-surface-env",
|
||||
- "existing_consumption": "Existing Deployment envFrom interface is reusable, but PAT delivery must wait for a reviewed, digest-pinned fixed image that prevents Authorization header forwarding on native HTTP redirects.",
|
||||
+ "existing_consumption": "Existing envFrom interface is reusable only after approved full fixed image plus runtime-owned exact14 URL/auth-env allowlist is active. Existing authenticated sources need separately reviewed safe inventory and migration before fail-closed rollout.",
|
||||
"policy_delta": {
|
||||
"file": "railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
"add_exact_stanza": "path \"platform/data/workloads/reuse/reuse-surface/forgejo-source-read\" { capabilities = [\"read\"] }",
|
||||
@@ -84,15 +84,39 @@
|
||||
},
|
||||
"fixed_image_gate": {
|
||||
"status": "pending owner patch and reviewed image",
|
||||
- "reason": "Reuse owner identified urllib Authorization forwarding on redirects in current native fetch implementation.",
|
||||
+ "reason": "Current native fetch permits registration-selected auth_env plus arbitrary URL and forwards Authorization on redirect. Both must be fixed before credential delivery.",
|
||||
"required": [
|
||||
- "reuse owner redirect patch and tests",
|
||||
- "exact source/patch digest",
|
||||
+ "reuse owner full redirect and auth_env/URL patch and meaningful boundary tests",
|
||||
+ "exact full source/patch digest",
|
||||
"approved image digest built from fixed source",
|
||||
- "runtime proof credentials cannot reach redirected destination",
|
||||
- "apps reviewed rollout with image change explicit"
|
||||
+ "runtime controlled exact14 auth allowlist config digest",
|
||||
+ "safe owner inventory and reviewed migration of existing authenticated sources",
|
||||
+ "runtime proof denied URLs are checked before credential lookup and authenticated redirects never forward credential",
|
||||
+ "apps reviewed rollout and rollback with image/config changes explicit"
|
||||
],
|
||||
- "current_image_accepts_new_pat": false
|
||||
+ "current_image_accepts_new_pat": false,
|
||||
+ "reviewed_candidate_inputs": {
|
||||
+ "consumer_owner_allowlist_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json",
|
||||
+ "consumer_owner_allowlist_sha256": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
+ "consumer_patch_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/reuse-owner-preparation-r1.patch",
|
||||
+ "consumer_patch_sha256": "f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e",
|
||||
+ "fixed_federation_source_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
+ "allowlist_semantic_crosscheck": "pass exact same env and exact14 URL sets; serialization ordering differs",
|
||||
+ "image_digest": null,
|
||||
+ "owner_review_status": "baseline changes_requested; full candidate must integrate and obtain runtime proof",
|
||||
+ "test_report": "owner reports focused30pass; whole226pass1isolatedrelativepath failure; no live runtime proof",
|
||||
+ "native_verification_helper_sha256": "c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c",
|
||||
+ "native_verification_helper_artifact": "/tmp/cust81-reuse-reader-owner/reuse_surface/private_source_verify.py"
|
||||
+ }
|
||||
+ },
|
||||
+ "runtime_auth_source_allowlist": {
|
||||
+ "env": "REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST",
|
||||
+ "candidate_value": "{\"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN\":[\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\",\"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738\"]}",
|
||||
+ "candidate_artifact": "builds/reuse-surface-private-source-reader/auth-source-allowlist.json",
|
||||
+ "artifact_sha256": "9a5d5a1ffb6ff1fa9e0cba7fb77779fd832c5bff413718d63c6f6e314ac653fc",
|
||||
+ "semantic_value_sha256": "30c823e0ccd4cd5bcccacfa731bcf0674b63ba6d5ec79f2c012ec650201f81f5",
|
||||
+ "policy": "Only runtime owner may configure; untrusted registration cannot grant itself authorization. Exact equality checked before any credential env lookup. Exact14 pinned index and entry API URLs only.",
|
||||
+ "existing_authenticated_sources": "Owner must safely inventory all authenticated sources using sanitized auth_env/URL metadata and migrate valid existing mappings before fail-closed rollout; do not expose secrets or blindly authorize inventory."
|
||||
}
|
||||
},
|
||||
"sources": [
|
||||
@@ -202,14 +226,17 @@
|
||||
"founder exact revision approval",
|
||||
"headroom/PVC/rollout preflight",
|
||||
"reuse owner redirect patch reviewed and fixed image approved",
|
||||
- "verify fixed image before projecting reader PAT into hub process"
|
||||
+ "verify fixed image before projecting reader PAT into hub process",
|
||||
+ "full consumer source-authorization patch reviewed, not redirect-only",
|
||||
+ "runtime owner exact14 allowlist and existing auth-source migration reviewed"
|
||||
],
|
||||
"verification": {
|
||||
"pre_apply": [
|
||||
"Pin exact CCR and plan revision/digest, owner reviews, founder disposition and rollout artifacts. Resolve existing identity/team/path conflicts before any mutation.",
|
||||
"Confirm live namespace/Deployment and its image digest, current ESO identity/binding and secret field presence through safe suppressing helper; no Secret dump.",
|
||||
"Check CPU headroom and rollout strategy/PVC constraints; retain exact prior source manifest/image and server dry-run/client diff.",
|
||||
- "Current native fetch redirect forwarding hazard must be fixed in the reviewed image before PAT delivery to the hub. An unchanged vulnerable image is not acceptable."
|
||||
+ "Current native fetch redirect forwarding hazard must be fixed in the reviewed image before PAT delivery to the hub. An unchanged vulnerable image is not acceptable.",
|
||||
+ "Full consumer patch must reject registration-controlled auth_env/URL destinations before credential lookup; configure owner-reviewed allowlist and migrate existing authenticated sources before rollout."
|
||||
],
|
||||
"positive": [
|
||||
"PAT metadata scope exactly read:repository; exact seven team repos with no other private grants.",
|
||||
@@ -221,17 +248,31 @@
|
||||
"Reader cannot fetch a known private repository outside the seven (preselected by platform using metadata, no unauthorized body retention).",
|
||||
"Forgejo writes, admin/user operations and non-code unit access denied; probe design must avoid creating writes if an unexpected permission exists.",
|
||||
"Default identity and scoped writer OIDC caller cannot read new KV path; ESO role cannot read any other sibling path, and wrong SA/namespace/audience cannot authenticate.",
|
||||
- "Native missing-auth and anonymous private-index fetch fail closed; no fallback to admin/personal/core-hub credentials."
|
||||
+ "Native missing-auth and anonymous private-index fetch fail closed; no fallback to admin/personal/core-hub credentials.",
|
||||
+ "Registration-selected auth_env or unlisted same-host/other-host/unpinned/wrong-query URL is refused before credential lookup; token value is never read or transmitted.",
|
||||
+ "Authenticated redirects fail closed without forwarding Authorization, including same-origin path changes. Runtime allowlist contains exact14 consented pinned URL strings only for new readerenv."
|
||||
],
|
||||
"receipts": "Sanitized IDs, repo names, scopes, hashes, status booleans and timestamps only; no credentials, raw errors, private index bodies or Secret objects."
|
||||
},
|
||||
"rollback": {
|
||||
"immediate": "Revoke this dedicated PAT and remove exact new team/user grants. Disable only private registrations created by the associated attempt after reconciling ambiguous requests; preserve all public registrations.",
|
||||
- "delivery": "Remove only new ES data item and exact new ESO policy stanza, restore prior source revision through owner path, delete only new materialized token key if needed using suppressed owner helper, then roll the reviewed fixed image or restore a prior image only after credential removal is verified. Do not delete reuse-surface-env or rotate existing writer/webhook fields.",
|
||||
+ "delivery": "Remove only new ES data item and exact new ESO policy stanza, restore prior source revision through owner path, delete only new materialized token key if needed using suppressed owner helper, then roll the reviewed fixed image or restore a prior image only after credential removal is verified. Do not delete reuse-surface-env or rotate existing writer/webhook fields. Remove only the new reader environment mapping from runtime source allowlist while preserving separately reviewed existing mappings; coordinate image/config rollback so stale deployments never receive the PAT.",
|
||||
"preserve_evidence": "Retain failed/partial outcomes, reconcile any ambiguous identity or token creation; never blindly remint after an observation timeout."
|
||||
},
|
||||
"cost_estimate_eur": 0,
|
||||
"cost_assumptions": "Incremental cloud infrastructure only; existing service/resources. Build and rollout of reviewed fixed image is required. Human labor/compute changes not priced; re-estimate if owner rollout changes resources.",
|
||||
"credible_loss_estimate_eur": null,
|
||||
- "risk_unpriced_reason": "Repository-wide private source exposure and availability impact cannot be bounded from local evidence; supervised execution required."
|
||||
+ "risk_unpriced_reason": "Repository-wide private source exposure and availability impact cannot be bounded from local evidence; supervised execution required.",
|
||||
+ "revision_history": [
|
||||
+ {
|
||||
+ "revision": 1,
|
||||
+ "spec_sha256": "0d1f604c76e086c82675dc535004cf4da51ef5dc247959fab50295a673407980",
|
||||
+ "status": "draft-unapproved-never-executed",
|
||||
+ "ref": "custodian owner-assembly/mason-r1/"
|
||||
+ },
|
||||
+ {
|
||||
+ "revision": 2,
|
||||
+ "reason": "Same proposal refined after consumer owner found URL/auth_env credential exfiltration in addition to redirect forwarding."
|
||||
+ }
|
||||
+ ]
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"observed_at": "2026-10-15T02:17:19.446055+00:00",
|
||||
"classification": "read_only_owner_correlation_completed_no_root_cause_proven",
|
||||
"node_external_ip_verified": true,
|
||||
"window_start": "2026-10-15T02:05:30Z",
|
||||
"window_end": "2026-10-15T02:11:10Z",
|
||||
"whitelisted_events": [
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "oidc_authorization_url_route",
|
||||
"timestamp": "2026-10-15T02:05:31.893682490Z",
|
||||
"event_count": 1
|
||||
},
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "oidc_authorization_url_route",
|
||||
"timestamp": "2026-10-15T02:08:47.819042830Z",
|
||||
"event_count": 1
|
||||
}
|
||||
],
|
||||
"component_observations": [
|
||||
{
|
||||
"component": "openbao",
|
||||
"category": "logs_observed",
|
||||
"event_count": 127,
|
||||
"bounded_bytes": 46712,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "openbao_other",
|
||||
"category": "logs_observed",
|
||||
"event_count": 0,
|
||||
"bounded_bytes": 115,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "logs_observed",
|
||||
"event_count": 65,
|
||||
"bounded_bytes": 18746,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 4,
|
||||
"bounded_bytes": 1162,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 0,
|
||||
"bounded_bytes": 0,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 1,
|
||||
"bounded_bytes": 176,
|
||||
"possible_truncation": false
|
||||
}
|
||||
],
|
||||
"installed_role_metadata": "Unauthenticated exact-role GET denied; no ambient credential/helper read and no authenticated fallback used.",
|
||||
"source_role_sha256": "418fa61d7674893535e20812f0151752861f1277e81249c337fdf819447843fe",
|
||||
"source_callback_checks": {
|
||||
"cli_localhost8250": true,
|
||||
"cli_loopback8250": true,
|
||||
"ui_tunnel_callback": true
|
||||
},
|
||||
"limits": [
|
||||
"No raw pod logs, exception, code, URL, email, session, Secret or token payload retained/output.",
|
||||
"No explicit OIDC callback, exchange, claim failure or identity LOGIN/CODE_TO_TOKEN markers observed. Normal server log verbosity may omit successful or failed authentication events.",
|
||||
"Authorization URL route markers alone do not prove browser completion, CLI callback, token issuance or revocation.",
|
||||
"Failed Warden attempts and absent child receipts supplied by root are consistent with child not running; server data cannot prove no session was issued.",
|
||||
"Installed role configuration remains unverified without a separately sanctioned attended metadata read."
|
||||
],
|
||||
"offline_fixture": "Three classifier fixtures passed constant-category-only checks",
|
||||
"supervision": "No own platform promotion record found; supervised read-only assignment, no inherited custodian authority.",
|
||||
"mutations_or_new_login": false,
|
||||
"progress_id": "1ba4fa3e-84a7-471f-8f73-123453fc665e"
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"window_start": "2026-10-15T02:05:30Z",
|
||||
"window_end": "2026-10-15T02:11:10Z",
|
||||
"mode": "read_only_suppressed_raw_logs",
|
||||
"node_verified": true,
|
||||
"components": [
|
||||
{
|
||||
"component": "openbao",
|
||||
"category": "logs_observed",
|
||||
"event_count": 127,
|
||||
"bounded_bytes": 46712,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "openbao_other",
|
||||
"category": "logs_observed",
|
||||
"event_count": 0,
|
||||
"bounded_bytes": 115,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "logs_observed",
|
||||
"event_count": 65,
|
||||
"bounded_bytes": 18746,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 4,
|
||||
"bounded_bytes": 1162,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 0,
|
||||
"bounded_bytes": 0,
|
||||
"possible_truncation": false
|
||||
},
|
||||
{
|
||||
"component": "keycape",
|
||||
"category": "logs_observed",
|
||||
"event_count": 1,
|
||||
"bounded_bytes": 176,
|
||||
"possible_truncation": false
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "oidc_authorization_url_route",
|
||||
"timestamp": "2026-10-15T02:05:31.893682490Z",
|
||||
"event_count": 1
|
||||
},
|
||||
{
|
||||
"component": "openbao_ui",
|
||||
"category": "oidc_authorization_url_route",
|
||||
"timestamp": "2026-10-15T02:08:47.819042830Z",
|
||||
"event_count": 1
|
||||
}
|
||||
],
|
||||
"installed_role_observation": "unauthenticated_read_denied"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"patch": "/tmp/cust81-platform-attended-runner-r2.patch",
|
||||
"patch_sha256": "045c26f9028a14c52cabf731bdb85b70a009f7eecd90e52f4b89474de955e465",
|
||||
"artifacts": {
|
||||
"scripts/apply-reuse-surface-reader-custody.py": "179bd66fc53b99f293ff8dc1fc5f9de004391e2e71705b468ca743c0cfb46600",
|
||||
"tests/test_reuse_surface_reader_custody_apply.py": "6b272b8c8be488594e43607f99a413595ec37e09900e60119f8f168c57e0c253",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json": "178dd2aeddf3544438d584fa16e3ceb1a541a357d4dff5b8b4b0e95db2f357ac",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/attended-custody-handoff-r1.json": "35962e2c39e92897fe479f2194dee2c832275978d435f9147498c9d6882b8fec",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/platform-attended-runner-review-r1.json": "02e7d947dc14e1ab3f20258b2e882e9253fef5e9c5685097a047a88f97442729"
|
||||
},
|
||||
"validation": "12runnerboundary tests pass;40total reader tests pass; preparation only, no login/credentials/mutations",
|
||||
"r1_preserved_patch": "/tmp/cust81-platform-attended-runner-r1.patch",
|
||||
"same_trial": "conditional approval-digest refinement of sameCCR0046; candidate unobserved/unapplied"
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"patch": "/tmp/cust81-platform-attended-runner-r3.patch",
|
||||
"patch_sha256": "8074bd4210e3265c64542a9f007f56369cc5c93badadd41c680168b5a2a3c5c3",
|
||||
"artifacts": {
|
||||
"scripts/apply-reuse-surface-reader-custody.py": "934b421c357e154f6508ab4b7c30f1aadfea2bf9a2f187d83968928d5eecad42",
|
||||
"tests/test_reuse_surface_reader_custody_apply.py": "cb2c4b5f8a96cfb71015d94c5d51de0e1bbf5c8e3d90e866b207a070c1a7a266",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json": "178dd2aeddf3544438d584fa16e3ceb1a541a357d4dff5b8b4b0e95db2f357ac",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/attended-custody-handoff-r1.json": "1247ad5aa73289a26a48e999b870ce2d60abb409132783e14456ea318689acff",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/platform-attended-runner-review-r1.json": "2406b70ed5a0fe755fe967d3568fabffc84396bb5509c55417aa6900dbb9a4d9"
|
||||
},
|
||||
"validation": "17 runnerboundary /45 allreader tests pass; preparation only, no login/credentials/mutations",
|
||||
"r2_preserved_patch": "/tmp/cust81-platform-attended-runner-r2.patch",
|
||||
"r2_changes_requested_receipt": "c4e61cd185bfc738fe8e2f7e89b71408ed35d441dfa38183cd83bad9c0832445",
|
||||
"same_trial": "ACLsnapshot/revalidation refinement of sameCCR0046; candidate remains unobserved/unapplied"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"record": "manual-reader-maintenance-preparation",
|
||||
"checked_at": "2026-10-15T00:02:09.905564+00:00",
|
||||
"source_commit": "cc3c7d883b8af64ca82352c3b9cd5e8f97982135",
|
||||
"published_identifier_commit": "352aeea5e9f3b5e6649ec00ba2b8003c10b472bc",
|
||||
"remote_main_verified": "352aeea5e9f3b5e6649ec00ba2b8003c10b472bc",
|
||||
"projection_status": "applied",
|
||||
"instance": "primary/railiance01",
|
||||
"task_id": "f0780e75-b0bd-5bf5-b926-c8413abb885c",
|
||||
"record_id": "RPF-WP-0058-T05",
|
||||
"task_status": "wait",
|
||||
"source_planned_due_date": "2027-01-13",
|
||||
"hub_due_date": null,
|
||||
"activation_date": null,
|
||||
"runtime_gate": "Before delivery set due to observed actual activation date+90, retain owner binding and verify projected date; no activation proven.",
|
||||
"cache_limitation": "Current projection left due_date null despite committed source field; live task description contains planned date. No manual cache patch; runtime gate remains unresolved.",
|
||||
"raw_receipt_sha256": "b8a7560cb6a13d67d8754f82be6c139579856894940db4529548819ec001de83",
|
||||
"workplan_sha256": "dc28f61af4ec44b7a9e9855035590ad07a6892212a132a90503b4d4727a17e38",
|
||||
"helper_artifacts_changed": false,
|
||||
"credentials_live_actions_performed": false,
|
||||
"progress_id": "fcdce94d-297c-41bb-bc21-443b96c54fa0"
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"root": "/tmp/cust81-platform-reader-owner",
|
||||
"patch": "/tmp/cust81-platform-reader-owner-preparation-r1.patch",
|
||||
"patch_sha256": "a94547b419b87e38dc178b02d1d31af61cc11a71e3734052a190167668db8e30",
|
||||
"artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "438f384215c7a1719c1e069dfa9723d5ddab4cfa9ceb64236acdb1cae4854f02",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "f7b4f2c7a0a85315d1229a1c96f8f0b255b9c17d77426d47f9b14df9a83787ae",
|
||||
"tests/test_reuse_surface_forgejo_source_provision.py": "2bf8c217e1a0a032dbd496afa1b9567a9f8d726a7a7e2e2dbde708e9ab44323d",
|
||||
"workplans/RPF-WP-0058-reuse-private-source-reader.md": "4693cd656d583fa9f833909efbbe39a876b06ea9db177f86089938f3992a91fd",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "1ca0192a5c066a1756f4d11a2fb840784c4a49088f77f575a6b2170f3a5ca47e",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/apps-externalsecret-data-item.draft.yaml": "e582a7d2eeb68668858ec19a6a32b8b816a4850b5ad1757f7686330b141578b2",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/README.md": "177fdef19f20c7327358cd70f3b612a6eff644dcac1a12fcb7c639369d903c0d",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/platform-owner-review-r1.json": "0a9746f65bb79982632accd9e20a2a632eafc5ee5410f4eddeadd625e73ca0bd"
|
||||
},
|
||||
"validation": {
|
||||
"targeted_unittest": "17 passed",
|
||||
"ccr_validator": "passed; bound_claims_confirmed false -> apply blocked",
|
||||
"git_diff_check": "passed"
|
||||
},
|
||||
"authority": "supervised preparation only; no live calls/token reads/credential mutation/cluster mutation/hub approvals/commit/push",
|
||||
"allocation": "CCR-2026-0046 and RPF-WP-0058 provisional; root must recheck current primary before integration"
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
id: CCR-2026-0046
|
||||
kind: credential-change-request
|
||||
schema_version: 1
|
||||
request_type: workload-kv-read
|
||||
title: Reuse Surface exact seven private-source repository reader
|
||||
status: proposed
|
||||
created: '2026-10-15'
|
||||
updated: '2026-10-15'
|
||||
requester:
|
||||
agent: platform-reader-owner delegated preparation worker
|
||||
reason: CUST-WP-0081 hosted63/70; founder metadata consent excludes credential provisioning.
|
||||
Prepare separate seven-source reader.
|
||||
review:
|
||||
required: true
|
||||
required_approvers:
|
||||
- platform-operator
|
||||
- railiance-forge-owner
|
||||
- reuse-surface-owner
|
||||
- railiance-apps-owner
|
||||
comments: []
|
||||
target:
|
||||
domain: infotech
|
||||
tenant: reuse
|
||||
workload: reuse-surface
|
||||
environment: production
|
||||
purpose: Native acquisition of seven owner-consented pinned capability indexes.
|
||||
Entire private repository code becomes readable by this dedicated reader; publication
|
||||
remains only approved metadata.
|
||||
openbao:
|
||||
mount: platform
|
||||
kv_path: platform/workloads/reuse/reuse-surface/forgejo-source-read
|
||||
fields:
|
||||
- FORGEJO_SOURCE_TOKEN
|
||||
- API_USER
|
||||
- API_BASE_URL
|
||||
- TOKEN_SCOPES
|
||||
- GENERATED_AT
|
||||
- CCR_ID
|
||||
policy_name: workload-kv-read-reuse-surface-runtime-eso
|
||||
policy_file: openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
auth:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
role: reuse-surface-runtime-eso
|
||||
bound_claims:
|
||||
service_account_names:
|
||||
- reuse-surface-eso
|
||||
service_account_namespaces:
|
||||
- reuse
|
||||
bound_claims_confirmed: false
|
||||
policies:
|
||||
- workload-kv-read-reuse-surface-runtime-eso
|
||||
ttl: 15m
|
||||
audience: openbao
|
||||
access_frontdoor:
|
||||
type: external-secrets
|
||||
catalog_id: reuse-surface-forgejo-source-read
|
||||
readiness: pending-review
|
||||
resolvable: false
|
||||
delivery:
|
||||
surface: external-secrets
|
||||
bootstrap_command: warden access openbao-platform-admin-login --exec -- python3
|
||||
scripts/provision-reuse-surface-forgejo-source.py --approval <approved-revision-receipt>
|
||||
--receipt <0600-durable-receipt>
|
||||
provisioning_script: scripts/provision-reuse-surface-forgejo-source.py
|
||||
target: Existing namespace-limited store openbao-reuse. Apps owner adds only proposed
|
||||
data item into ExternalSecret reuse/reuse-surface-runtime. Shared target reuse-surface-env
|
||||
remains intact.
|
||||
forgejo_identity: reuse-surface-source
|
||||
forgejo_team: reuse-surface-source-readers
|
||||
repositories:
|
||||
- coulomb/rapp-openbao
|
||||
- coulomb/rapp-policy-nexus
|
||||
- coulomb/rapp-postgres
|
||||
- coulomb/rapp-sbom-nexus
|
||||
- coulomb/rapp-telemetry
|
||||
- coulomb/rapp-tenant-engine
|
||||
- coulomb/rapp-user-engine
|
||||
token_scopes:
|
||||
- read:repository
|
||||
consumer_gate: Reader must not be delivered until reuse-surface owner supplies verified
|
||||
image digest containing BOTH authenticated redirect rejection/HTTPS enforcement
|
||||
AND runtime-controlled exact URL allowlist checked before credential lookup. Apps
|
||||
owner must deploy REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST mapping the reader environment
|
||||
to exactly fourteen consented pinned index/entry URLs and inventory existing authenticated
|
||||
sources for migration. Current image and redirect-only fix are not approved. Native
|
||||
owner helper must verify all seven pinned indexes and entry hashes after rollout.
|
||||
risk:
|
||||
classification: high
|
||||
cost_estimate_eur: 0
|
||||
notes:
|
||||
- Repository grants disclose entire source of these seven repositories to hub; PAT
|
||||
cannot restrict access to index files.
|
||||
- No all-repositories membership, unrelated private grants, admin privileges or
|
||||
repository writes.
|
||||
- Existing ESO role expands by one exact data-read path; writer OIDC caller remains
|
||||
unchanged and denied new path.
|
||||
- Current consumer accepts arbitrary authenticated source URLs and redirects, allowing
|
||||
bearer disclosure. Delivery barred until full reviewed redirect/HTTPS and runtime-controlled
|
||||
exact URL allowlist patch image plus config is verified.
|
||||
- PAT has no server-side expiry; rotate quarterly and immediately upon incident.
|
||||
- Bootstrap writes only exact new identity/team/PAT/KV. Policy apply, ESO delivery,
|
||||
consumer rollout and registrations require their separate reviewed owner steps.
|
||||
verification:
|
||||
positive:
|
||||
- All seven repositories private; exact team/code-read flags, one dedicated team
|
||||
member, exact seven memberships, effective permissions read and exactly one read:repository
|
||||
PAT.
|
||||
- Each consented index digest matches from authenticated Forgejo read; final hosted
|
||||
native owner helper verifies pinned entry hashes as well.
|
||||
- OpenBao stdin CAS=0 field write readback matches without printing or persisting
|
||||
value.
|
||||
- ESO binding and namespace condition surveyed and confirmed; native fixed consumer
|
||||
image verified before delivery; final hosted70/70 exactly once.
|
||||
negative:
|
||||
- Known excluded private rapp-core-hub reader request denied (403/404); administrative
|
||||
endpoint and Actions secrets denied.
|
||||
- No additional private effective repositories or teams; no write probing that could
|
||||
itself cause a change.
|
||||
- Scoped writer and default OpenBao identities denied new data path; ESO role denied
|
||||
unrelated siblings and wrong SA/namespace/audience login refused.
|
||||
- Missing/anonymous native authentication refused with no privileged fallback.
|
||||
activation_conditions:
|
||||
- Independent platform/Forgejo/reuse/apps owner reviews of exact artifacts and founder
|
||||
CCR provisioning disposition recorded; no approval inferred from metadata consent.
|
||||
- Confirm existing ESO Kubernetes binding before any delegated metadata apply.
|
||||
- Consumer security patch image digest verified and apps rollout artifacts reviewed/approved
|
||||
before delivery.
|
||||
- Durable bounded bootstrap and positive/negative receipts pass; owner native fetch
|
||||
proof passes all seven pinned sources.
|
||||
- Separate scoped writer registration batch followed by fresh hosted70/70 completion
|
||||
audit.
|
||||
evidence: []
|
||||
lifecycle:
|
||||
deactivate: Revoke dedicated PAT through attended Forgejo owner; remove exact new
|
||||
ESO data item and exact new policy stanza through owners. Preserve writer/webhook
|
||||
paths and public registrations.
|
||||
rotate: Attended owner revokes old identified PAT then governed bootstrap remints
|
||||
and verifies; never overwrite differing/orphan KV through bootstrap. Explicit
|
||||
scoped CAS update plan required for rotation.
|
||||
compromised: Revoke all dedicated service-user PATs, remove delivery data item,
|
||||
roll fixed consumer with owners, retain failed sanitized evidence; investigate
|
||||
all seven private source exposure. Do not rotate unrelated credentials without
|
||||
separate scope.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Seven private-source reader preparation
|
||||
|
||||
This bundle is prepared under platform's INTENT custody/Q1 remit. Nothing here
|
||||
records founder credential approval or another owner's review. The existing
|
||||
all-repository core-hub provisioner is precedent only and is never executed.
|
||||
|
||||
The exact bounded command, after approved CCR, independent reviews and binding
|
||||
confirmation, is:
|
||||
|
||||
```sh
|
||||
warden access openbao-platform-admin-login --exec -- python3 scripts/provision-reuse-surface-forgejo-source.py --approval /absolute/path/approved-artifact-receipt.json --receipt /absolute/path/durable-0600-receipt.jsonl
|
||||
```
|
||||
|
||||
An approval receipt must contain `ccr_id: CCR-2026-0046`, `decision: approved`,
|
||||
nonempty `supervisor` and `decision_ref`, and `approved_artifacts` mapping exactly
|
||||
these four relative paths to SHA256 digests of their approved bytes:
|
||||
|
||||
- credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml
|
||||
- scripts/provision-reuse-surface-forgejo-source.py
|
||||
- docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json
|
||||
- openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
|
||||
CCR itself must be approved; preparation hashes do not satisfy that gate. This
|
||||
local check binds revisions and relies on attended platform lane/supervisor
|
||||
custody for authenticity; it is not a signed approval authority or an autopilot
|
||||
grant. Metadata apply is separately governed by existing credential-change CLI.
|
||||
Bootstrap never applies policy or changes cluster delivery.
|
||||
|
||||
`apps-externalsecret-data-item.draft.yaml` is a proposed insertion for apps owner,
|
||||
not a deployable manifest or an apps review. Existing ESO role reads only one new
|
||||
exact data path; caller writer policy is unchanged. Claim confirmation remains
|
||||
false until live owner survey. Consumer delivery must wait for the FULL reviewed
|
||||
image fixing redirects/HTTPS and runtime-controlled exact source URL allowlist,
|
||||
with fourteen consented pinned URLs in REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST.
|
||||
|
||||
Failure recovery: inspect sanitized journal and authoritative identity/team/PAT/
|
||||
KV state quietly. A missing observation is not proof a request did not apply.
|
||||
The script refuses orphan PAT/KV state rather than reminting. Revoke only an
|
||||
identified dedicated PAT through Forgejo owner's attended recovery; if mint
|
||||
response lost, locate its exact recorded attempt-name prefix before revocation.
|
||||
Never delete unrelated token or shared runtime Secret. Rotation/recovery of an
|
||||
existing KV version requires a separately scoped CAS update plan; this create-only
|
||||
bootstrap does not overwrite or automatically rollback ambiguous writes.
|
||||
|
||||
Known execution gates: provisional IDs must be rechecked against current primary;
|
||||
Forgejo owner must confirm the API contracts (team organization username, complete
|
||||
sudo user inventory and token_last_eight metadata) on the deployed server; missing
|
||||
or differing metadata fails closed. Excluded-private rapp-core-hub must still be
|
||||
private. Native verification is a separate consumer-owned proof, not this API
|
||||
bootstrap's index-only custody check. New reader provisioning/production approval,
|
||||
apps config delta, fixed-image digest, live ESO survey and final70 audit all pending.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
secretKey: REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN
|
||||
remoteRef:
|
||||
key: workloads/reuse/reuse-surface/forgejo-source-read
|
||||
property: FORGEJO_SOURCE_TOKEN
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
"id": "CCR-2026-0046-BOOTSTRAP-R1",
|
||||
"revision": 1,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"capability_ids": [
|
||||
"capability.railiance.openbao-package"
|
||||
],
|
||||
"consented_commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.policy-publication-package"
|
||||
],
|
||||
"consented_commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"capability_ids": [
|
||||
"capability.railiance.postgres-isolation-recovery"
|
||||
],
|
||||
"consented_commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.sbom-runtime-package"
|
||||
],
|
||||
"consented_commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"capability_ids": [
|
||||
"capability.railiance.telemetry-package"
|
||||
],
|
||||
"consented_commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.tenant-engine-package"
|
||||
],
|
||||
"consented_commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.user-engine-package"
|
||||
],
|
||||
"consented_commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
}
|
||||
],
|
||||
"negative_private_probe": "rapp-core-hub",
|
||||
"identity": {
|
||||
"forgejo_user": "reuse-surface-source",
|
||||
"forgejo_org": "coulomb",
|
||||
"forgejo_team": "reuse-surface-source-readers",
|
||||
"restricted": true,
|
||||
"is_admin": false,
|
||||
"visibility": "private",
|
||||
"password": "random value retained only in attended process memory; never stored in evidence",
|
||||
"team_permission": "read",
|
||||
"includes_all_repositories": false,
|
||||
"can_create_org_repo": false,
|
||||
"units_map": {
|
||||
"repo.code": "read",
|
||||
"repo.actions": "none",
|
||||
"repo.packages": "none",
|
||||
"repo.issues": "none",
|
||||
"repo.ext_issues": "none",
|
||||
"repo.wiki": "none",
|
||||
"repo.pulls": "none",
|
||||
"repo.releases": "none",
|
||||
"repo.projects": "none",
|
||||
"repo.ext_wiki": "none"
|
||||
},
|
||||
"team_repositories": [
|
||||
"coulomb/rapp-openbao",
|
||||
"coulomb/rapp-policy-nexus",
|
||||
"coulomb/rapp-postgres",
|
||||
"coulomb/rapp-sbom-nexus",
|
||||
"coulomb/rapp-telemetry",
|
||||
"coulomb/rapp-tenant-engine",
|
||||
"coulomb/rapp-user-engine"
|
||||
],
|
||||
"token_scopes": [
|
||||
"read:repository"
|
||||
],
|
||||
"token_name_prefix": "reuse-surface-source-read-",
|
||||
"reuse_other_workload_token": false,
|
||||
"fail_on_differing_existing_identity_or_team": true,
|
||||
"enforcement": "Verify complete team membership and all effective user repository grants; no other team, collaborator or organisation grants may provide extra private read access."
|
||||
},
|
||||
"delivery": {
|
||||
"kv_mount": "platform",
|
||||
"kv_path": "platform/workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"field": "FORGEJO_SOURCE_TOKEN",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"eso_role": "reuse-surface-runtime-eso",
|
||||
"eso_identity": "reuse/reuse-surface-eso",
|
||||
"audience": "openbao",
|
||||
"ttl": "15m",
|
||||
"cluster_secret_store": "openbao-reuse",
|
||||
"store_namespace_condition": [
|
||||
"reuse"
|
||||
],
|
||||
"external_secret": "reuse/reuse-surface-runtime",
|
||||
"target_secret": "reuse/reuse-surface-env",
|
||||
"existing_consumption": "Deployment reuse/reuse-surface envFrom secretRef reuse-surface-env; environment variable available only after controlled rollout. Existing image and resource requests retained.",
|
||||
"policy_delta": {
|
||||
"file": "railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
"add_exact_stanza": "path \"platform/data/workloads/reuse/reuse-surface/forgejo-source-read\" { capabilities = [\"read\"] }",
|
||||
"retain_existing_paths": true,
|
||||
"do_not_change": "Caller-facing workload-kv-read-reuse-surface-runtime policy or writer OIDC role; those remain unable to read this new path."
|
||||
},
|
||||
"external_secret_data_delta": {
|
||||
"secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"remoteRef": {
|
||||
"key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"property": "FORGEJO_SOURCE_TOKEN"
|
||||
}
|
||||
},
|
||||
"delivery_design_reason": "Uses the existing namespace-limited ESO store and identity plus the existing envFrom interface. Separate new KV path keeps scoped writer callers from also acquiring private-source access. No new static ESO token or new store is needed.",
|
||||
"native_source_auth": {
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"auth_header": "Authorization",
|
||||
"header_behavior": "Native federation _auth_headers prefixes a bare token with Bearer; owner must verify accepted Forgejo raw API route with this actual native implementation."
|
||||
},
|
||||
"rollout_image": "Pending full consumer fix reviewed image digest; same-image rollout of current consumer is prohibited."
|
||||
},
|
||||
"consumer_gate": "Reader must not be delivered until reuse-surface owner supplies verified image digest containing BOTH authenticated redirect rejection/HTTPS enforcement AND runtime-controlled exact URL allowlist checked before credential lookup. Apps owner must deploy REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST mapping the reader environment to exactly fourteen consented pinned index/entry URLs and inventory existing authenticated sources for migration. Current image and redirect-only fix are not approved. Native owner helper must verify all seven pinned indexes and entry hashes after rollout.",
|
||||
"approval": "No approval recorded. External supervisor approval receipt must bind exactly CCR, script, contract and ESO policy bytes; CCR must be approved. Additional forge/apps/reuse owner reviews and rollout approval remain required.",
|
||||
"metadata_consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
"preparation_sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99",
|
||||
"recovery": "On ambiguous mutation stop. Reconcile complete Forgejo identity/team/repository/PAT metadata and exact KV field through suppressed helper before retry. Token without KV or reverse refuses automatically; revoke identified dedicated PAT through attended Forgejo owner recovery before retry. Never remint on observation timeout. KV CAS=0 prevents overwrites. Do not delete shared runtime Secret.",
|
||||
"consumer_preparation": {
|
||||
"owner": "reuse-surface",
|
||||
"allowlist_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json",
|
||||
"allowlist_sha256": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
"federation_py_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
"integration_patch_sha256": "f707ddfd36d5d470c176530671bb30ff6a3af2dfff86937aa8a5618bcb79768e",
|
||||
"review_state": "changes_requested for current consumer; preparation patch alone is not deployed-image evidence"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"id": "RPF-WP-0058-PLATFORM-OWNER-REVIEW-R1",
|
||||
"owner_repo": "railiance-platform",
|
||||
"reviewer": "platform-reader-owner delegated worker",
|
||||
"basis": [
|
||||
"INTENT.md shared stateful service custody, Q1 approval/delegated apply/revocation, stable delivery interface",
|
||||
"AGENTS.md credential-change owner review and founder-gated attended provisioning",
|
||||
"No own supervision record exists; supervised preparation only"
|
||||
],
|
||||
"disposition": "prepared_with_conditions_not_execution_approval",
|
||||
"scope": "Only platform custody/CCR/policy/bootstrap; other repo reviews not authored",
|
||||
"findings": [
|
||||
"Separate exact KV path and exact data-read ESO delta preserve writer-only caller boundary.",
|
||||
"No all-repo Forgejo team; preflight rejects different existing user/team, unexpected team repos/members, other teams/orgs/private grants, orphan PAT/KV, and unverified PAT/KV binding.",
|
||||
"Quiet create-only bootstrap with per-user lane lock, durable fsynced intents/outcomes, CAS=0 KV; uncertainty is reconciled before retry, never blindly reminted.",
|
||||
"Full consumer redirect/HTTPS plus authenticated URL allowlist image and exact config mandatory before credential delivery.",
|
||||
"Approval receipt is locally bound revision evidence, not authenticated authority; attended founder/platform lane remains authorization source."
|
||||
],
|
||||
"validation": {
|
||||
"targeted_tests": 17,
|
||||
"result": "passed",
|
||||
"ccr_validator": "passed with expected unconfirmed ESO binding/apply blocked warning",
|
||||
"diff_check": "passed"
|
||||
},
|
||||
"pending": [
|
||||
"Allocation recheck against owner primary before integration",
|
||||
"Independent Forgejo/apps/reuse owner reviews and actual founder credential provisioning decision",
|
||||
"Live ESO claims and namespace condition confirmation; no token read or live mutations performed",
|
||||
"Forgejo deployed API metadata contract support proof; sudo inventory and token_last_eight missing metadata fail closed",
|
||||
"Apps fixedimage digest/config/migration/dryrun/headroom rollout approval and receipt",
|
||||
"Native seven index+entry verification and final hosted70/70 audit"
|
||||
],
|
||||
"execution_approved": false,
|
||||
"live_changes_performed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
path "platform/data/workloads/reuse/reuse-surface/runtime-secrets" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/lookup-self" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/revoke-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
# CCR-2026-0046 proposed; exact extra data path, no metadata/list grants.
|
||||
path "platform/data/workloads/reuse/reuse-surface/forgejo-source-read" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Silent attended CCR-2026-0046 bootstrap; never runs without exact approval.
|
||||
|
||||
Only this seven-repository identity/PAT/KV path is mutable. No policy, ESO,
|
||||
consumer rollout, or registration mutations occur here. A receipt is evidence,
|
||||
not authorization. Uncertain requests are recorded and reconciled, never retried
|
||||
in the same execution; orphan PAT drift requires attended recovery.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import base64
|
||||
import datetime as dt
|
||||
import fcntl
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CCR = 'CCR-2026-0046'
|
||||
CCR_FILE = 'credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml'
|
||||
PLAN_FILE = 'docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json'
|
||||
SCRIPT_FILE = 'scripts/provision-reuse-surface-forgejo-source.py'
|
||||
API = 'https://forgejo.coulomb.social/api/v1'
|
||||
USER = 'reuse-surface-source'
|
||||
TEAM = 'reuse-surface-source-readers'
|
||||
ORG = 'coulomb'
|
||||
KV = 'platform/workloads/reuse/reuse-surface/forgejo-source-read'
|
||||
FIELD = 'FORGEJO_SOURCE_TOKEN'
|
||||
PREFIX = 'reuse-surface-source-read-'
|
||||
REPOS = frozenset('rapp-openbao rapp-policy-nexus rapp-postgres rapp-sbom-nexus rapp-telemetry rapp-tenant-engine rapp-user-engine'.split())
|
||||
SCOPES = ['read:repository']
|
||||
UNITS = {'repo.code': 'read', **{x: 'none' for x in ('repo.actions', 'repo.packages', 'repo.issues', 'repo.ext_issues', 'repo.wiki', 'repo.pulls', 'repo.releases', 'repo.projects', 'repo.ext_wiki')}}
|
||||
|
||||
class Refused(Exception):
|
||||
"""Messages must be fixed non-secret codes, never provider text."""
|
||||
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, *args, **kwargs):
|
||||
raise Refused('redirect_refused')
|
||||
|
||||
class Journal:
|
||||
def __init__(self, path):
|
||||
flags = os.O_RDWR | os.O_CREAT | os.O_APPEND | os.O_NOFOLLOW
|
||||
self.fd = os.open(path, flags, 0o600)
|
||||
if os.fstat(self.fd).st_mode & 0o077:
|
||||
os.close(self.fd)
|
||||
raise Refused('receipt_permissions')
|
||||
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
self.attempt = secrets.token_hex(12)
|
||||
def put(self, event, **fields):
|
||||
# Only locally authored operation tags, scalar status/IDs are accepted.
|
||||
allowed = {'operation', 'status', 'http_status', 'token_id', 'team_id', 'outcome', 'error_code'}
|
||||
if set(fields) - allowed:
|
||||
raise Refused('receipt_fields')
|
||||
record = {'at': dt.datetime.now(dt.timezone.utc).isoformat(), 'ccr': CCR,
|
||||
'attempt': self.attempt, 'event': event, **fields}
|
||||
data = (json.dumps(record, sort_keys=True) + '\n').encode()
|
||||
while data:
|
||||
written = os.write(self.fd, data)
|
||||
if written <= 0:
|
||||
raise Refused('receipt_write_failed')
|
||||
data = data[written:]
|
||||
os.fsync(self.fd)
|
||||
def close(self):
|
||||
os.close(self.fd)
|
||||
|
||||
class Client:
|
||||
def __init__(self, admin, journal):
|
||||
self.admin, self.journal = admin, journal
|
||||
self.opener = urllib.request.build_opener(NoRedirect())
|
||||
def request(self, method, path, payload=None, expected=(200,), *, token=None, sudo=False, basic=None, operation=None):
|
||||
if not path.startswith('/') or '://' in path:
|
||||
raise Refused('invalid_api_path')
|
||||
headers = {'Authorization': basic or 'token ' + (self.admin if token is None else token), 'Accept': 'application/json'}
|
||||
if sudo:
|
||||
headers['Sudo'] = USER
|
||||
if payload is not None:
|
||||
headers['Content-Type'] = 'application/json'
|
||||
if method != 'GET':
|
||||
if not operation:
|
||||
raise Refused('mutation_without_intent')
|
||||
self.journal.put('intent', operation=operation)
|
||||
req = urllib.request.Request(API + path, data=None if payload is None else json.dumps(payload).encode(), headers=headers, method=method)
|
||||
try:
|
||||
with self.opener.open(req, timeout=30) as response:
|
||||
status, body = response.status, response.read(2_000_001)
|
||||
except urllib.error.HTTPError as error:
|
||||
status, body = error.code, b''
|
||||
except Exception:
|
||||
if operation:
|
||||
self.journal.put('outcome', operation=operation, status='ambiguous')
|
||||
raise Refused('request_observation_failed') from None
|
||||
if operation:
|
||||
self.journal.put('outcome', operation=operation, status='observed', http_status=status)
|
||||
if status not in expected:
|
||||
raise Refused('unexpected_http_status')
|
||||
if len(body) > 2_000_000:
|
||||
raise Refused('response_bound_exceeded')
|
||||
if not body:
|
||||
return status, None
|
||||
try:
|
||||
return status, json.loads(body)
|
||||
except Exception:
|
||||
raise Refused('invalid_json') from None
|
||||
def pages(self, path, *, sudo=False):
|
||||
items = []
|
||||
for page in range(1, 21):
|
||||
_, batch = self.request('GET', path + f'?limit=100&page={page}', sudo=sudo)
|
||||
if not isinstance(batch, list) or any(not isinstance(x, dict) for x in batch):
|
||||
raise Refused('invalid_list')
|
||||
items.extend(batch)
|
||||
if len(batch) < 100:
|
||||
return items
|
||||
raise Refused('pagination_bound_exceeded')
|
||||
|
||||
class Bao:
|
||||
@staticmethod
|
||||
def run(args, stdin=None):
|
||||
try:
|
||||
return subprocess.run(['bao', *args], input=stdin, capture_output=True, timeout=60, check=False, cwd=ROOT)
|
||||
except Exception:
|
||||
raise Refused('bao_observation_failed') from None
|
||||
def get(self):
|
||||
result = self.run(['kv', 'get', '-format=json', KV])
|
||||
if result.returncode != 0:
|
||||
# Distinguish an actual not-found response from denied/unavailable.
|
||||
# Never include captured raw errors in any evidence.
|
||||
if re.search(rb'Code:\s*404\b', result.stderr or b'') or b'No value found at ' in (result.stderr or b''):
|
||||
return None
|
||||
raise Refused('bao_preflight_failed')
|
||||
try:
|
||||
data = json.loads(result.stdout)['data']['data']
|
||||
except Exception:
|
||||
raise Refused('bao_invalid_record') from None
|
||||
if set(data) != {FIELD, 'API_USER', 'API_BASE_URL', 'TOKEN_SCOPES', 'GENERATED_AT', 'CCR_ID'} or data['API_USER'] != USER or data['API_BASE_URL'] != 'https://forgejo.coulomb.social' or data['TOKEN_SCOPES'] != 'read:repository' or data['CCR_ID'] != CCR or not isinstance(data[FIELD], str) or not data[FIELD]:
|
||||
raise Refused('bao_record_conflict')
|
||||
return data
|
||||
def put(self, token, journal):
|
||||
journal.put('intent', operation='kv_create')
|
||||
timestamp = dt.datetime.now(dt.timezone.utc).isoformat()
|
||||
try:
|
||||
result = self.run(['kv', 'put', '-cas=0', KV, FIELD + '=-', 'API_USER=' + USER,
|
||||
'API_BASE_URL=https://forgejo.coulomb.social', 'TOKEN_SCOPES=read:repository',
|
||||
'GENERATED_AT=' + timestamp, 'CCR_ID=' + CCR], token.encode())
|
||||
except Refused:
|
||||
journal.put('outcome', operation='kv_create', status='ambiguous')
|
||||
raise
|
||||
journal.put('outcome', operation='kv_create', status='observed' if result.returncode == 0 else 'ambiguous')
|
||||
# Readback reconciles even a response failure after successful write.
|
||||
stored = self.get()
|
||||
if stored is None or not secrets.compare_digest(stored[FIELD], token):
|
||||
raise Refused('bao_write_requires_reconciliation')
|
||||
journal.put('verified', operation='kv_create', status='matched')
|
||||
|
||||
|
||||
def valid_user(user):
|
||||
return isinstance(user, dict) and user.get('login') == USER and user.get('restricted') is True and user.get('is_admin') is False and user.get('visibility') == 'private'
|
||||
|
||||
def valid_team(team):
|
||||
return isinstance(team, dict) and isinstance(team.get('id'), int) and team.get('name') == TEAM and team.get('permission') == 'read' and team.get('includes_all_repositories') is False and team.get('can_create_org_repo') is False and team.get('units_map') == UNITS
|
||||
|
||||
def repo_names(items):
|
||||
names = set()
|
||||
for repo in items:
|
||||
if repo.get('owner', {}).get('login') != ORG or repo.get('name') not in REPOS:
|
||||
raise Refused('unexpected_team_repository')
|
||||
names.add(repo['name'])
|
||||
if len(names) != len(items):
|
||||
raise Refused('duplicate_repository')
|
||||
return names
|
||||
|
||||
def inspect(client, bao):
|
||||
"""Complete read-only preflight before first mutation, including drift."""
|
||||
for repo in sorted(REPOS):
|
||||
_, meta = client.request('GET', f'/repos/{ORG}/{repo}')
|
||||
if not isinstance(meta, dict) or meta.get('private') is not True or meta.get('full_name') != ORG + '/' + repo:
|
||||
raise Refused('source_repository_conflict')
|
||||
_, probe = client.request('GET', '/repos/coulomb/rapp-core-hub')
|
||||
if not isinstance(probe, dict) or probe.get('private') is not True:
|
||||
raise Refused('negative_probe_not_private')
|
||||
_, user = client.request('GET', '/users/' + USER, expected=(200, 404))
|
||||
if user is not None and not valid_user(user):
|
||||
raise Refused('user_conflict')
|
||||
teams = [x for x in client.pages(f'/orgs/{ORG}/teams') if x.get('name') == TEAM]
|
||||
if len(teams) > 1:
|
||||
raise Refused('duplicate_team')
|
||||
team = teams[0] if teams else None
|
||||
members, repos = [], set()
|
||||
if team is not None:
|
||||
if not valid_team(team):
|
||||
raise Refused('team_conflict')
|
||||
members = client.pages('/teams/' + str(team['id']) + '/members')
|
||||
if len(members) > 1 or any(x.get('login') != USER for x in members):
|
||||
raise Refused('team_members_conflict')
|
||||
repos = repo_names(client.pages('/teams/' + str(team['id']) + '/repos'))
|
||||
tokens = []
|
||||
if user is not None:
|
||||
orgs = client.pages('/user/orgs', sudo=True)
|
||||
if any(x.get('username') != ORG for x in orgs):
|
||||
raise Refused('extra_organization_affiliation')
|
||||
grants = client.pages('/user/teams', sudo=True)
|
||||
if any(x.get('name') != TEAM or x.get('organization', {}).get('username') != ORG for x in grants):
|
||||
raise Refused('extra_team_grant')
|
||||
for meta in client.pages('/user/repos', sudo=True):
|
||||
if meta.get('private') is True and meta.get('full_name') not in {ORG + '/' + x for x in REPOS}:
|
||||
raise Refused('extra_private_repository_grant')
|
||||
if meta.get('owner', {}).get('login') == USER:
|
||||
raise Refused('service_user_owned_repository')
|
||||
tokens = client.pages('/users/' + USER + '/tokens')
|
||||
if len(tokens) > 1 or any(not str(x.get('name', '')).startswith(PREFIX) or x.get('scopes') != SCOPES or not isinstance(x.get('id'), int) for x in tokens):
|
||||
raise Refused('token_conflict')
|
||||
stored = bao.get()
|
||||
if bool(tokens) != bool(stored):
|
||||
raise Refused('orphan_token_or_kv_requires_recovery')
|
||||
if tokens and (not isinstance(tokens[0].get('token_last_eight'), str) or len(tokens[0]['token_last_eight']) != 8 or not secrets.compare_digest(tokens[0]['token_last_eight'], stored[FIELD][-8:])):
|
||||
raise Refused('pat_kv_binding_unverifiable')
|
||||
if tokens and (not team or repos != REPOS or len(members) != 1):
|
||||
raise Refused('active_lane_grant_drift')
|
||||
return user, team, repos, members, tokens, stored
|
||||
|
||||
def verify(client, source_token, plan):
|
||||
for source in plan['sources']:
|
||||
_, repo = client.request('GET', '/repos/' + ORG + '/' + source['repo'], token=source_token)
|
||||
if repo.get('permissions') != {'admin': False, 'push': False, 'pull': True}:
|
||||
raise Refused('effective_permission_not_read')
|
||||
# Exact consented bytes fetched quietly; raw output is never retained.
|
||||
path = f"/repos/{ORG}/{source['repo']}/contents/registry/indexes/capabilities.yaml?ref={source['consented_commit']}"
|
||||
_, content = client.request('GET', path, token=source_token)
|
||||
try:
|
||||
raw = base64.b64decode(content['content'], validate=False)
|
||||
except Exception:
|
||||
raise Refused('invalid_index_encoding') from None
|
||||
if hashlib.sha256(raw).hexdigest() != source['index_sha256']:
|
||||
raise Refused('index_digest_mismatch')
|
||||
client.request('GET', f"/repos/{ORG}/{source['repo']}/actions/secrets?limit=1", token=source_token, expected=(403, 404))
|
||||
client.request('GET', '/admin/users', token=source_token, expected=(403, 404))
|
||||
# A known private excluded source is selected by reviewed contract.
|
||||
probe = plan['negative_private_probe']
|
||||
_, meta = client.request('GET', '/repos/' + ORG + '/' + probe)
|
||||
if not isinstance(meta, dict) or meta.get('private') is not True:
|
||||
raise Refused('negative_probe_not_private')
|
||||
client.request('GET', '/repos/' + ORG + '/' + probe, token=source_token, expected=(403, 404))
|
||||
client.journal.put('verified', operation='reader_scope', status='seven_exact_no_extra_private_grants')
|
||||
|
||||
def provision(client, bao, plan):
|
||||
user, team, repos, members, tokens, stored = inspect(client, bao)
|
||||
if tokens:
|
||||
verify(client, stored[FIELD], plan)
|
||||
client.journal.put('terminal', outcome='verified_noop')
|
||||
return 'noop'
|
||||
if user is None:
|
||||
_, user = client.request('POST', '/admin/users', {'username': USER, 'email': USER + '@coulomb.social', 'full_name': 'Reuse Surface seven-source reader', 'password': secrets.token_urlsafe(48), 'restricted': True, 'must_change_password': False, 'send_notify': False, 'visibility': 'private'}, expected=(201,), operation='user_create')
|
||||
if not valid_user(user):
|
||||
raise Refused('created_user_contract_failed')
|
||||
if team is None:
|
||||
_, team = client.request('POST', f'/orgs/{ORG}/teams', {'name': TEAM, 'description': CCR + ' exact seven consented source repos', 'permission': 'read', 'includes_all_repositories': False, 'can_create_org_repo': False, 'units': ['repo.code'], 'units_map': UNITS}, expected=(201,), operation='team_create')
|
||||
if not valid_team(team):
|
||||
raise Refused('created_team_contract_failed')
|
||||
team_id = team['id']
|
||||
for repo in sorted(REPOS - repos):
|
||||
client.request('PUT', f'/teams/{team_id}/repos/{ORG}/{repo}', expected=(204,), operation='team_repo_' + repo)
|
||||
if not members:
|
||||
client.request('PUT', f'/teams/{team_id}/members/{USER}', expected=(204,), operation='team_member_add')
|
||||
# Recheck complete grants before creating any PAT; refuse implicit grants.
|
||||
_, _, actual_repos, actual_members, _, _ = inspect(client, bao)
|
||||
if actual_repos != REPOS or len(actual_members) != 1:
|
||||
raise Refused('incomplete_team_scope')
|
||||
password = secrets.token_urlsafe(48)
|
||||
client.request('PATCH', '/admin/users/' + USER, {'password': password, 'restricted': True, 'admin': False, 'allow_create_organization': False, 'allow_git_hook': False, 'allow_import_local': False, 'max_repo_creation': 0, 'must_change_password': False, 'active': True, 'visibility': 'private'}, operation='service_password_randomize')
|
||||
basic = 'Basic ' + base64.b64encode((USER + ':' + password).encode()).decode()
|
||||
_, record = client.request('POST', '/users/' + USER + '/tokens', {'name': PREFIX + client.journal.attempt, 'scopes': SCOPES}, expected=(201,), basic=basic, operation='pat_create')
|
||||
if not isinstance(record, dict) or not isinstance(record.get('id'), int) or record.get('scopes') != SCOPES or not isinstance(record.get('sha1'), str) or not record['sha1']:
|
||||
raise Refused('created_pat_requires_recovery')
|
||||
client.journal.put('identified', operation='pat_create', token_id=record['id'], team_id=team_id)
|
||||
token = record['sha1']
|
||||
verify(client, token, plan)
|
||||
bao.put(token, client.journal)
|
||||
# Repeat complete grant inventory after custody; verify exactly one PAT.
|
||||
inspect(client, bao)
|
||||
client.journal.put('terminal', outcome='provisioned_verified_delivery_pending')
|
||||
return 'minted'
|
||||
|
||||
def approval(path):
|
||||
try:
|
||||
receipt = json.loads(Path(path).read_text())
|
||||
ccr = yaml.safe_load((ROOT / CCR_FILE).read_text())
|
||||
if receipt['ccr_id'] != CCR or receipt['decision'] != 'approved' or not receipt['supervisor'] or not receipt['decision_ref'] or ccr['status'] != 'approved':
|
||||
raise Refused('approval_missing')
|
||||
files = {CCR_FILE, PLAN_FILE, SCRIPT_FILE, 'openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl'}
|
||||
if set(receipt['approved_artifacts']) != files:
|
||||
raise Refused('approval_artifact_set')
|
||||
for rel in files:
|
||||
if hashlib.sha256((ROOT / rel).read_bytes()).hexdigest() != receipt['approved_artifacts'][rel]:
|
||||
raise Refused('approval_revision_mismatch')
|
||||
plan = json.loads((ROOT / PLAN_FILE).read_text())
|
||||
if {x['repo'] for x in plan['sources']} != REPOS or len(plan['sources']) != 7 or plan['negative_private_probe'] != 'rapp-core-hub' or any(not re.fullmatch(r'[a-f0-9]{40}', x['consented_commit']) or not re.fullmatch(r'[a-f0-9]{64}', x['index_sha256']) for x in plan['sources']):
|
||||
raise Refused('plan_scope_conflict')
|
||||
return plan
|
||||
except Refused:
|
||||
raise
|
||||
except Exception:
|
||||
raise Refused('approval_invalid') from None
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--approval', required=True)
|
||||
parser.add_argument('--receipt', required=True)
|
||||
parser.add_argument('--inner', action='store_true', help=argparse.SUPPRESS)
|
||||
args = parser.parse_args(argv)
|
||||
journal = None
|
||||
lane_lock = None
|
||||
try:
|
||||
plan = approval(args.approval)
|
||||
if not args.inner:
|
||||
# Governed OpenBao attended lane surrounds this command. Warden
|
||||
# supplies only Forgejo API_TOKEN to inner child, not via argv/files.
|
||||
result = subprocess.run(['warden', 'access', 'forgejo-admin-api-token', '--field', 'API_TOKEN', '--exec', '--', sys.executable, str(ROOT / SCRIPT_FILE), '--inner', '--approval', str(Path(args.approval).resolve()), '--receipt', str(Path(args.receipt).resolve())], capture_output=True, timeout=900, check=False, cwd=ROOT)
|
||||
return 0 if result.returncode == 0 else 1
|
||||
lane_lock = os.open('/tmp/' + CCR + '-reuse-reader-' + str(os.getuid()) + '.lock', os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600)
|
||||
if os.fstat(lane_lock).st_uid != os.getuid() or os.fstat(lane_lock).st_mode & 0o077:
|
||||
raise Refused('lane_lock_owner_or_permissions')
|
||||
fcntl.flock(lane_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
journal = Journal(args.receipt)
|
||||
admin = os.environ.get('API_TOKEN')
|
||||
if not admin:
|
||||
raise Refused('admin_route_missing')
|
||||
provision(Client(admin, journal), Bao(), plan)
|
||||
return 0
|
||||
except Exception as error:
|
||||
if journal:
|
||||
journal.put('terminal', outcome='stopped_reconcile_before_retry', error_code=str(error) if isinstance(error, Refused) else 'internal_failure')
|
||||
return 1
|
||||
finally:
|
||||
if journal:
|
||||
journal.close()
|
||||
if lane_lock is not None:
|
||||
os.close(lane_lock)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
result = main()
|
||||
except Exception:
|
||||
result = 1
|
||||
raise SystemExit(result)
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
"""Meaningful fail-closed tests; no credentials or network are used."""
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import hashlib
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
from unittest.mock import patch
|
||||
|
||||
spec = importlib.util.spec_from_file_location('reuse_provision', Path(__file__).parents[1] / 'scripts/provision-reuse-surface-forgejo-source.py')
|
||||
p = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(p)
|
||||
|
||||
class Journal:
|
||||
attempt = 'mock-attempt'
|
||||
def __init__(self): self.rows = []
|
||||
def put(self, event, **kw): self.rows.append((event, kw))
|
||||
|
||||
class Fake:
|
||||
def __init__(self):
|
||||
self.journal = Journal()
|
||||
self.user = None
|
||||
self.team = None
|
||||
self.members = []
|
||||
self.repos = []
|
||||
self.tokens = []
|
||||
self.extra = []
|
||||
self.mutations = []
|
||||
def pages(self, path, sudo=False):
|
||||
if path == '/orgs/coulomb/teams': return [] if self.team is None else [self.team]
|
||||
if path.endswith('/members'): return self.members
|
||||
if path.endswith('/repos') and path.startswith('/teams/'): return self.repos
|
||||
if path == '/user/orgs': return [] if not self.members else [{'username':'coulomb'}]
|
||||
if path == '/user/teams': return [] if not self.members else [{**self.team,'organization':{'username':'coulomb'}}]
|
||||
if path == '/user/repos': return self.repos + self.extra
|
||||
if path.endswith('/tokens'): return self.tokens
|
||||
raise AssertionError(path)
|
||||
def request(self, method, path, payload=None, expected=(200,), **kwargs):
|
||||
if method != 'GET':
|
||||
self.mutations.append((method,path))
|
||||
raise AssertionError('mutation must not occur in preflight failure test')
|
||||
if path == '/users/' + p.USER: return (404,None) if self.user is None else (200,self.user)
|
||||
if path.startswith('/repos/'): return 200, {'private':True,'full_name':'coulomb/'+path.split('/')[3]}
|
||||
raise AssertionError(path)
|
||||
|
||||
class Bao:
|
||||
def __init__(self, data=None): self.data = data
|
||||
def get(self): return self.data
|
||||
|
||||
class Tests(unittest.TestCase):
|
||||
def complete(self):
|
||||
c = Fake()
|
||||
c.user = {'login':p.USER,'restricted':True,'is_admin':False,'visibility':'private'}
|
||||
c.team = {'id':10,'name':p.TEAM,'permission':'read','includes_all_repositories':False,'can_create_org_repo':False,'units_map':p.UNITS}
|
||||
c.members = [{'login':p.USER}]
|
||||
c.repos = [{'name':x,'owner':{'login':p.ORG},'private':True,'full_name':p.ORG+'/'+x} for x in sorted(p.REPOS)]
|
||||
return c
|
||||
def test_existing_all_repository_team_refused_before_mutation(self):
|
||||
c=self.complete(); c.team['includes_all_repositories']=True
|
||||
with self.assertRaisesRegex(p.Refused,'team_conflict'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_excluded_team_repo_refused_before_mutation(self):
|
||||
c=self.complete(); c.repos.append({'name':'rapp-core-hub','owner':{'login':'coulomb'}})
|
||||
with self.assertRaisesRegex(p.Refused,'unexpected_team_repository'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_admin_user_refused_before_mutation(self):
|
||||
c=self.complete(); c.user['is_admin']=True
|
||||
with self.assertRaisesRegex(p.Refused,'user_conflict'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_token_kv_binding_mismatch_refused(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'different'}]
|
||||
with self.assertRaisesRegex(p.Refused,'pat_kv_binding_unverifiable'): p.provision(c,Bao({p.FIELD:'mock-token'}),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_extra_private_grants_refused(self):
|
||||
c=self.complete(); c.extra=[{'private':True,'full_name':'coulomb/secret-outside'}]
|
||||
with self.assertRaisesRegex(p.Refused,'extra_private'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_orphan_pat_not_reminted(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'ck-token'}]
|
||||
with self.assertRaisesRegex(p.Refused,'orphan'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_verified_noop_rechecks_secret_and_scope(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'ck-token'}]
|
||||
with patch.object(p,'verify') as verify:
|
||||
self.assertEqual(p.provision(c,Bao({p.FIELD:'mock-token'}),{}),'noop')
|
||||
verify.assert_called_once_with(c,'mock-token',{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
self.assertEqual(c.journal.rows[-1],('terminal',{'outcome':'verified_noop'}))
|
||||
def test_existing_write_unit_refused(self):
|
||||
c=self.complete(); c.team['units_map']={**p.UNITS,'repo.code':'write'}
|
||||
with self.assertRaisesRegex(p.Refused,'team_conflict'): p.inspect(c,Bao())
|
||||
def test_api_ambiguous_mutation_records_intent_without_raw_error(self):
|
||||
j=Journal(); c=p.Client('fake-secret-admin',j)
|
||||
with patch.object(c.opener,'open',side_effect=RuntimeError('fake-secret-admin sensitive provider body')):
|
||||
with self.assertRaisesRegex(p.Refused,'request_observation_failed'):
|
||||
c.request('POST','/admin/users',{'password':'fake-secret-password'},operation='user_create')
|
||||
self.assertEqual([x[0] for x in j.rows],['intent','outcome'])
|
||||
self.assertNotIn('secret',json.dumps(j.rows))
|
||||
self.assertEqual(j.rows[-1][1]['status'],'ambiguous')
|
||||
def test_network_error_never_reaches_stdout_stderr(self):
|
||||
out,err=io.StringIO(),io.StringIO()
|
||||
with patch.object(p,'approval',return_value={}), patch.object(p,'Journal',return_value=Journal()) as factory, patch.dict(p.os.environ,{'API_TOKEN':'fake-secret-admin'}), patch.object(p,'provision',side_effect=RuntimeError('fake-secret-admin body')):
|
||||
factory.return_value.close=lambda:None
|
||||
with redirect_stdout(out),redirect_stderr(err):
|
||||
code=p.main(['--inner','--approval','mock','--receipt','mock'])
|
||||
self.assertEqual(code,1); self.assertEqual(out.getvalue()+err.getvalue(),'')
|
||||
self.assertNotIn('secret',json.dumps(factory.return_value.rows))
|
||||
def test_unapproved_ccr_refuses_before_warden(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
f=Path(tmp)/'approval.json'; f.write_text(json.dumps({'ccr_id':p.CCR,'decision':'approved','supervisor':'mock','decision_ref':'mock'}))
|
||||
with patch.object(p.subprocess,'run') as run:
|
||||
self.assertEqual(p.main(['--approval',str(f),'--receipt',str(Path(tmp)/'receipt')]),1)
|
||||
run.assert_not_called()
|
||||
def test_approved_artifact_revision_mismatch_before_warden(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp); files={p.CCR_FILE,p.PLAN_FILE,p.SCRIPT_FILE,'openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl'}
|
||||
for rel in files:
|
||||
f=root/rel; f.parent.mkdir(parents=True,exist_ok=True); f.write_text('mock')
|
||||
(root/p.CCR_FILE).write_text(yaml.safe_dump({'status':'approved'}))
|
||||
receipt={'ccr_id':p.CCR,'decision':'approved','supervisor':'founder','decision_ref':'mock-resolved-decision','approved_artifacts':{rel:hashlib.sha256((root/rel).read_bytes()).hexdigest() for rel in files}}
|
||||
f=root/'approval.json'; f.write_text(json.dumps(receipt)); (root/p.SCRIPT_FILE).write_text('modified-after-approval')
|
||||
with patch.object(p,'ROOT',root),patch.object(p.subprocess,'run') as run:
|
||||
self.assertEqual(p.main(['--approval',str(f),'--receipt',str(root/'receipt')]),1)
|
||||
run.assert_not_called()
|
||||
def test_journal_symlink_refused(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
f=Path(tmp)/'real'; f.write_text(''); link=Path(tmp)/'link'; link.symlink_to(f)
|
||||
with self.assertRaises(OSError): p.Journal(link)
|
||||
def test_redirects_never_follow_auth_header(self):
|
||||
with self.assertRaisesRegex(p.Refused,'redirect_refused'):
|
||||
p.NoRedirect().redirect_request(None,None,302,'',{ },'https://other.example')
|
||||
def test_bao_cas_write_uncertain_response_reconciled(self):
|
||||
b=p.Bao(); j=Journal()
|
||||
with patch.object(b,'run',return_value=type('R',(),{'returncode':1})()) as run, patch.object(b,'get',return_value={p.FIELD:'fake-source'}):
|
||||
b.put('fake-source',j)
|
||||
self.assertIn('-cas=0',run.call_args.args[0]); self.assertEqual(run.call_args.args[1],b'fake-source')
|
||||
self.assertEqual(j.rows[-1],('verified',{'operation':'kv_create','status':'matched'}))
|
||||
def test_bao_timeout_preserves_ambiguous_outcome(self):
|
||||
b=p.Bao(); j=Journal()
|
||||
with patch.object(b,'run',side_effect=p.Refused('bao_observation_failed')):
|
||||
with self.assertRaises(p.Refused): b.put('fake-source',j)
|
||||
self.assertEqual(j.rows[-1],('outcome',{'operation':'kv_create','status':'ambiguous'}))
|
||||
def test_kv_denial_not_treated_as_absence(self):
|
||||
b=p.Bao()
|
||||
with patch.object(b,'run',return_value=type('R',(),{'returncode':1,'stderr':b'Code: 403 Error: fake-secret'})()):
|
||||
with self.assertRaisesRegex(p.Refused,'bao_preflight_failed'): b.get()
|
||||
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
id: RPF-WP-0058
|
||||
type: workplan
|
||||
title: Prepare the exact seven private-source reader for reuse-surface
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: proposed
|
||||
owner: codex
|
||||
topic_slug: platform
|
||||
created: '2026-10-15'
|
||||
updated: '2026-10-15'
|
||||
flavor: implementation
|
||||
depends_on: []
|
||||
---
|
||||
|
||||
Custody/Q1 work under platform INTENT.md: dependable shared secret custody and
|
||||
stable delivery with reviewed, delegated apply and revocation. Founder metadata
|
||||
consent is recorded separately and grants no credential or live rollout authority.
|
||||
Preparation occurs in isolated clone; no own supervision record means supervised
|
||||
preparation only. IDs CCR-2026-0046 and RPF-WP-0058 are provisional until integration
|
||||
rechecks the authoritative owner's latest allocation.
|
||||
|
||||
## Prepare bounded custody change
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Prepare CCR-2026-0046, exact seven-source bootstrap contract, silent bounded
|
||||
provisioner, tests and exact ESO policy delta. No provisioner invocation against
|
||||
live service, token read, policy apply, or cluster change has occurred.
|
||||
|
||||
## Independent owner review and founder disposition
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T02
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Platform prepared-review covers own custody artifacts. Forge, apps and consumer
|
||||
owners independently review their scopes; their reviews remain pending. Founder
|
||||
credential provisioning approval must bind exact artifact hashes and confirmed
|
||||
ESO claims. No approval inferred from metadata disclosure. CCR stays proposed.
|
||||
Current consumer image cannot receive this credential: full authenticated
|
||||
redirect/HTTPS plus exact source URL allowlist patch and runtime config required.
|
||||
|
||||
## Execute and verify restricted reader custody
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T03
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
After review and disposition, attended platform lane executes silent bootstrap.
|
||||
Read-only preflight checks all seven private repos, restricted non-admin private
|
||||
user, exact code-read team flags, no unrelated members/teams/private grants and
|
||||
PAT/KV drift. Mutation intent and observed/ambiguous outcome are fsynced. No
|
||||
uncertain mint is retried. CAS=0 never overwrites existing KV. Noop still verifies
|
||||
PAT metadata binding, all seven pinned index digests and excluded-private/admin/
|
||||
Actions denial. Raw provider output and credentials remain suppressed.
|
||||
|
||||
## Owner delivery and hosted completion
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T04
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Apps owner integrates proposed data item and exact fourteen approved URL config,
|
||||
performs approved fixed-image rollout after CPU/PVC preflight and safe diff/dryrun.
|
||||
Confirm existing ESO SA reuse/reuse-surface-eso, role, audience and namespace
|
||||
condition from live metadata. Consumer owner native verification precedes private
|
||||
registration; Custodian final fresh hosted audit must prove all70 exactly once.
|
||||
Removing reader grants and delivery must preserve shared writer/webhook fields.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"root": "/tmp/cust81-platform-reader-owner",
|
||||
"revision": 2,
|
||||
"same_proposal": "CCR-2026-0046; refinement, not new trial",
|
||||
"patch": "/tmp/cust81-platform-reader-owner-preparation-r2.patch",
|
||||
"patch_sha256": "2b529b722d5c3797cd68900aa025919430bdfd3bc4551e214c5d79ed77d5329e",
|
||||
"artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "90522912fb78fed72e625382fb7114a0e9aafe76d59e36bffecab83c5bb7d643",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "abf60a754596baa1610d23b1fd367385b0222ac8aeaebba0f2cfb7ec4c62055b",
|
||||
"scripts/revoke-reuse-surface-forgejo-source.py": "f78d1b26fbff93c6cc481a896d9fb9b9ce93049b8b3267fa41a64bc57dadb162",
|
||||
"tests/test_reuse_surface_forgejo_source_provision.py": "0d06fd22596a0f17ecd8dd5ff6a12bb1b1782258d00126ad468661099d7d298b",
|
||||
"tests/test_reuse_surface_forgejo_source_recovery.py": "08247129b220c198d8f7d1322ce63956203b9ffcaa0342e52990bb6520fd138d",
|
||||
"workplans/RPF-WP-0058-reuse-private-source-reader.md": "9aeb0fbbb5359b7ba9afb4f5f7f2c55f84dade0d6d95fe98b15901e656aed5b5",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "2bb72671e0a5f230497ab8ef2967a20ee68d1f2cf081f07c84f6db3442e18d8b",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/apps-externalsecret-data-item.draft.yaml": "e582a7d2eeb68668858ec19a6a32b8b816a4850b5ad1757f7686330b141578b2",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/README.md": "dcc8879d96b85b21b52c6cac9d7a58db78169ed903490ef11561bf8d6aa507e5",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/platform-owner-review-r1.json": "0a9746f65bb79982632accd9e20a2a632eafc5ee5410f4eddeadd625e73ca0bd",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/platform-owner-review-r2.json": "a25c765bfddc114f3a70947142ad702515635ee36d5f24e2f6e2505661e49fe7"
|
||||
},
|
||||
"r1_preserved_patch": "/tmp/cust81-platform-reader-owner-preparation-r1.patch",
|
||||
"r1_preserved_sha256": "a94547b419b87e38dc178b02d1d31af61cc11a71e3734052a190167668db8e30",
|
||||
"r1_confirmed_defect": "Premature completion on server-capped50page omitted latergrants; endpointowned-onlyclaim disproved by version-source and corrected.",
|
||||
"validation": {
|
||||
"targeted_unittest": "28 passed (23 provision,5 recovery)",
|
||||
"ccr_validator": "passed; bound_claims_confirmed false -> apply blocked",
|
||||
"git_diff_check": "passed"
|
||||
},
|
||||
"authority": "supervised preparation only; no live calls/token reads/credential mutation/cluster mutation/hub approvals/commit/push",
|
||||
"allocation": "CCR-2026-0046 and RPF-WP-0058 provisional; root must recheck current primary before integration"
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
id: CCR-2026-0046
|
||||
kind: credential-change-request
|
||||
schema_version: 1
|
||||
request_type: workload-kv-read
|
||||
title: Reuse Surface exact seven private-source repository reader
|
||||
status: proposed
|
||||
created: '2026-10-15'
|
||||
updated: '2026-10-15'
|
||||
requester:
|
||||
agent: platform-reader-owner delegated preparation worker
|
||||
reason: CUST-WP-0081 hosted63/70; founder metadata consent excludes credential provisioning.
|
||||
Prepare separate seven-source reader.
|
||||
review:
|
||||
required: true
|
||||
required_approvers:
|
||||
- platform-operator
|
||||
- railiance-forge-owner
|
||||
- reuse-surface-owner
|
||||
- railiance-apps-owner
|
||||
comments: []
|
||||
target:
|
||||
domain: infotech
|
||||
tenant: reuse
|
||||
workload: reuse-surface
|
||||
environment: production
|
||||
purpose: Native acquisition of seven owner-consented pinned capability indexes.
|
||||
Entire private repository code becomes readable by this dedicated reader; publication
|
||||
remains only approved metadata.
|
||||
openbao:
|
||||
mount: platform
|
||||
kv_path: platform/workloads/reuse/reuse-surface/forgejo-source-read
|
||||
fields:
|
||||
- FORGEJO_SOURCE_TOKEN
|
||||
- API_USER
|
||||
- API_BASE_URL
|
||||
- TOKEN_SCOPES
|
||||
- GENERATED_AT
|
||||
- CCR_ID
|
||||
policy_name: workload-kv-read-reuse-surface-runtime-eso
|
||||
policy_file: openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
auth:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
role: reuse-surface-runtime-eso
|
||||
bound_claims:
|
||||
service_account_names:
|
||||
- reuse-surface-eso
|
||||
service_account_namespaces:
|
||||
- reuse
|
||||
bound_claims_confirmed: false
|
||||
policies:
|
||||
- workload-kv-read-reuse-surface-runtime-eso
|
||||
ttl: 15m
|
||||
audience: openbao
|
||||
access_frontdoor:
|
||||
type: external-secrets
|
||||
catalog_id: reuse-surface-forgejo-source-read
|
||||
readiness: pending-review
|
||||
resolvable: false
|
||||
delivery:
|
||||
surface: external-secrets
|
||||
bootstrap_command: warden access openbao-platform-admin-login --exec -- python3
|
||||
scripts/provision-reuse-surface-forgejo-source.py --approval <approved-revision-receipt>
|
||||
--receipt <0600-durable-receipt>
|
||||
provisioning_script: scripts/provision-reuse-surface-forgejo-source.py
|
||||
target: Existing namespace-limited store openbao-reuse. Apps owner adds only proposed
|
||||
data item into ExternalSecret reuse/reuse-surface-runtime. Shared target reuse-surface-env
|
||||
remains intact.
|
||||
forgejo_identity: reuse-surface-source
|
||||
forgejo_team: reuse-surface-source-readers
|
||||
repositories:
|
||||
- coulomb/rapp-openbao
|
||||
- coulomb/rapp-policy-nexus
|
||||
- coulomb/rapp-postgres
|
||||
- coulomb/rapp-sbom-nexus
|
||||
- coulomb/rapp-telemetry
|
||||
- coulomb/rapp-tenant-engine
|
||||
- coulomb/rapp-user-engine
|
||||
token_scopes:
|
||||
- read:repository
|
||||
consumer_gate: Reader must not be delivered until reuse-surface owner supplies verified
|
||||
image digest containing BOTH authenticated redirect rejection/HTTPS enforcement
|
||||
AND runtime-controlled exact URL allowlist checked before credential lookup. Apps
|
||||
owner must deploy REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST mapping the reader environment
|
||||
to exactly fourteen consented pinned index/entry URLs and inventory existing authenticated
|
||||
sources for migration. Current image and redirect-only fix are not approved. Native
|
||||
owner helper must verify all seven pinned indexes and entry hashes after rollout.
|
||||
risk:
|
||||
classification: high
|
||||
cost_estimate_eur: 0
|
||||
notes:
|
||||
- Repository grants disclose entire source of these seven repositories to hub; PAT
|
||||
cannot restrict access to index files.
|
||||
- No all-repositories membership, unrelated private grants, admin privileges or
|
||||
repository writes.
|
||||
- Existing ESO role expands by one exact data-read path; writer OIDC caller remains
|
||||
unchanged and denied new path.
|
||||
- Current consumer accepts arbitrary authenticated source URLs and redirects, allowing
|
||||
bearer disclosure. Delivery barred until full reviewed redirect/HTTPS and runtime-controlled
|
||||
exact URL allowlist patch image plus config is verified.
|
||||
- PAT has no server-side expiry; rotate quarterly and immediately upon incident.
|
||||
- Bootstrap writes only exact new identity/team/PAT/KV. Policy apply, ESO delivery,
|
||||
consumer rollout and registrations require their separate reviewed owner steps.
|
||||
- R1 pagination defect preserved; R2 pages until empty with duplicate/count/bound
|
||||
checks. Deployed-version live complete-grant inventory remains required; code
|
||||
review is not live scope proof.
|
||||
- Quarterly rotation is a proposed manual requirement, not deployed expiry enforcement;
|
||||
due-date owner task/receipt and separate exact-token recovery approval must be
|
||||
established before activation.
|
||||
verification:
|
||||
positive:
|
||||
- All seven repositories private; exact team/code-read flags, one dedicated team
|
||||
member, exact seven memberships, effective permissions read and exactly one read:repository
|
||||
PAT.
|
||||
- Each consented index digest matches from authenticated Forgejo read; final hosted
|
||||
native owner helper verifies pinned entry hashes as well.
|
||||
- OpenBao stdin CAS=0 field write readback matches without printing or persisting
|
||||
value.
|
||||
- ESO binding and namespace condition surveyed and confirmed; native fixed consumer
|
||||
image verified before delivery; final hosted70/70 exactly once.
|
||||
negative:
|
||||
- Known excluded private rapp-core-hub reader request denied (403/404); administrative
|
||||
endpoint and Actions secrets denied.
|
||||
- No additional private effective repositories or teams; no write probing that could
|
||||
itself cause a change.
|
||||
- Scoped writer and default OpenBao identities denied new data path; ESO role denied
|
||||
unrelated siblings and wrong SA/namespace/audience login refused.
|
||||
- Missing/anonymous native authentication refused with no privileged fallback.
|
||||
activation_conditions:
|
||||
- Independent platform/Forgejo/reuse/apps owner reviews of exact artifacts and founder
|
||||
CCR provisioning disposition recorded; no approval inferred from metadata consent.
|
||||
- Confirm existing ESO Kubernetes binding before any delegated metadata apply.
|
||||
- Consumer security patch image digest verified and apps rollout artifacts reviewed/approved
|
||||
before delivery.
|
||||
- Durable bounded bootstrap and positive/negative receipts pass; owner native fetch
|
||||
proof passes all seven pinned sources.
|
||||
- Separate scoped writer registration batch followed by fresh hosted70/70 completion
|
||||
audit.
|
||||
evidence: []
|
||||
lifecycle:
|
||||
deactivate: Separate explicitly approved silent scripts/revoke-reuse-surface-forgejo-source.py
|
||||
validates reviewed single PAT ID/name, admin PATCH only dedicated user to fresh
|
||||
process-memory password, Basic-auth DELETE exact token, then reconciles complete
|
||||
inventory absence. Admin PAT/Sudo alone cannot delete. KV/shared runtime Secret
|
||||
preserved. Apps/platform owners remove only new ESO item/policy stanza through
|
||||
their reviewed path; preserve writer/webhook/public registrations. Recovery helper
|
||||
approval is separate from mint/bootstrap approval.
|
||||
rotate: Attended owner revokes old identified PAT then governed bootstrap remints
|
||||
and verifies; never overwrite differing/orphan KV through bootstrap. Explicit
|
||||
scoped CAS update plan required for rotation.
|
||||
compromised: Revoke all dedicated service-user PATs, remove delivery data item,
|
||||
roll fixed consumer with owners, retain failed sanitized evidence; investigate
|
||||
all seven private source exposure. Do not rotate unrelated credentials without
|
||||
separate scope.
|
||||
revision: 2
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
# Seven private-source reader preparation
|
||||
|
||||
This bundle is prepared under platform's INTENT custody/Q1 remit. Nothing here
|
||||
records founder credential approval or another owner's review. The existing
|
||||
all-repository core-hub provisioner is precedent only and is never executed.
|
||||
|
||||
The exact bounded command, after approved CCR, independent reviews and binding
|
||||
confirmation, is:
|
||||
|
||||
```sh
|
||||
warden access openbao-platform-admin-login --exec -- python3 scripts/provision-reuse-surface-forgejo-source.py --approval /absolute/path/approved-artifact-receipt.json --receipt /absolute/path/durable-0600-receipt.jsonl
|
||||
```
|
||||
|
||||
An approval receipt must contain `ccr_id: CCR-2026-0046`, `decision: approved`,
|
||||
nonempty `supervisor` and `decision_ref`, and `approved_artifacts` mapping exactly
|
||||
these four relative paths to SHA256 digests of their approved bytes:
|
||||
|
||||
- credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml
|
||||
- scripts/provision-reuse-surface-forgejo-source.py
|
||||
- docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json
|
||||
- openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl
|
||||
|
||||
CCR itself must be approved; preparation hashes do not satisfy that gate. This
|
||||
local check binds revisions and relies on attended platform lane/supervisor
|
||||
custody for authenticity; it is not a signed approval authority or an autopilot
|
||||
grant. Metadata apply is separately governed by existing credential-change CLI.
|
||||
Bootstrap never applies policy or changes cluster delivery.
|
||||
|
||||
`apps-externalsecret-data-item.draft.yaml` is a proposed insertion for apps owner,
|
||||
not a deployable manifest or an apps review. Existing ESO role reads only one new
|
||||
exact data path; caller writer policy is unchanged. Claim confirmation remains
|
||||
false until live owner survey. Consumer delivery must wait for the FULL reviewed
|
||||
image fixing redirects/HTTPS and runtime-controlled exact source URL allowlist,
|
||||
with fourteen consented pinned URLs in REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST.
|
||||
|
||||
Failure recovery: inspect sanitized journal and authoritative identity/team/PAT/
|
||||
KV state quietly. A missing observation is not proof a request did not apply.
|
||||
The script refuses orphan PAT/KV state rather than reminting. Revoke only an
|
||||
identified dedicated PAT through Forgejo owner's attended recovery; if mint
|
||||
response lost, locate its exact recorded attempt-name prefix before revocation.
|
||||
Never delete unrelated token or shared runtime Secret. Rotation/recovery of an
|
||||
existing KV version requires a separately scoped CAS update plan; this create-only
|
||||
bootstrap does not overwrite or automatically rollback ambiguous writes.
|
||||
|
||||
Known execution gates: provisional IDs must be rechecked against current primary;
|
||||
Forgejo owner must confirm the API contracts (team organization username, complete
|
||||
sudo user inventory and token_last_eight metadata) on the deployed server; missing
|
||||
or differing metadata fails closed. Excluded-private rapp-core-hub must still be
|
||||
private. Native verification is a separate consumer-owned proof, not this API
|
||||
bootstrap's index-only custody check. New reader provisioning/production approval,
|
||||
apps config delta, fixed-image digest, live ESO survey and final70 audit all pending.
|
||||
|
||||
Revision2 refines the same CCR after a confirmed server-capped pagination defect.
|
||||
R1 patch and failed review evidence remain retained; no new scored trial is claimed.
|
||||
All list requests now continue until an observed empty page, reject duplicate IDs,
|
||||
and reconcile supplied total counts. Forgejo v11.0.3 source confirms user/repos
|
||||
includes effective direct/team grants. Stable ID sort is requested for that list.
|
||||
Version compatibility and live proof still precede apply.
|
||||
|
||||
Forgejo11.0.3 token DELETE requires Basic authentication, unlike token inventory GET.
|
||||
Admin PAT/Sudo alone is not a valid recovery implementation. The separate reviewed
|
||||
recovery must reset only this dedicated user password to fresh process-memory bytes,
|
||||
Basic-auth DELETE only the reviewed token ID, and verify absence. Recovery
|
||||
helper approval and manual rotation due-date task remain activation gates; no
|
||||
deployed quarterly expiry enforcement is claimed.
|
||||
|
||||
|
||||
The separately reviewed executable credential rollback is:
|
||||
|
||||
```sh
|
||||
warden access openbao-platform-admin-login --exec -- python3 scripts/revoke-reuse-surface-forgejo-source.py --approval /absolute/path/approved-exact-token-recovery.json --receipt /absolute/path/durable-0600-recovery.jsonl
|
||||
```
|
||||
|
||||
Its independent approval record names `action: revoke_exact_pat`, the CCR,
|
||||
`decision: approved`, supervisor and decision_ref, `target: {id: <reviewed id>,
|
||||
name: <exact recorded attempt name>}`, and SHA256s in `approved_artifacts` for
|
||||
exactly recovery helper, bootstrap helper, CCR and bootstrap contract. This
|
||||
receipt also relies on attended supervisor custody; it is not signed authority.
|
||||
Different inventory stops before password mutation. Password stays in memory;
|
||||
DELETE uses Basic authorization captured only by the quiet client. Uncertain
|
||||
DELETE is followed only by read-only absence reconciliation, never another
|
||||
DELETE or mint. KV is deliberately preserved, so rotation needs its separate CAS
|
||||
plan. Credential rollback and apps delivery cleanup have independent receipts.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
secretKey: REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN
|
||||
remoteRef:
|
||||
key: workloads/reuse/reuse-surface/forgejo-source-read
|
||||
property: FORGEJO_SOURCE_TOKEN
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
{
|
||||
"id": "CCR-2026-0046-BOOTSTRAP-R2",
|
||||
"revision": 2,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"capability_ids": [
|
||||
"capability.railiance.openbao-package"
|
||||
],
|
||||
"consented_commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.policy-publication-package"
|
||||
],
|
||||
"consented_commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"capability_ids": [
|
||||
"capability.railiance.postgres-isolation-recovery"
|
||||
],
|
||||
"consented_commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.sbom-runtime-package"
|
||||
],
|
||||
"consented_commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"capability_ids": [
|
||||
"capability.railiance.telemetry-package"
|
||||
],
|
||||
"consented_commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.tenant-engine-package"
|
||||
],
|
||||
"consented_commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.user-engine-package"
|
||||
],
|
||||
"consented_commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
}
|
||||
],
|
||||
"negative_private_probe": "rapp-core-hub",
|
||||
"identity": {
|
||||
"forgejo_user": "reuse-surface-source",
|
||||
"forgejo_org": "coulomb",
|
||||
"forgejo_team": "reuse-surface-source-readers",
|
||||
"restricted": true,
|
||||
"is_admin": false,
|
||||
"visibility": "private",
|
||||
"password": "random value retained only in attended process memory; never stored in evidence",
|
||||
"team_permission": "read",
|
||||
"includes_all_repositories": false,
|
||||
"can_create_org_repo": false,
|
||||
"units_map": {
|
||||
"repo.code": "read",
|
||||
"repo.actions": "none",
|
||||
"repo.packages": "none",
|
||||
"repo.issues": "none",
|
||||
"repo.ext_issues": "none",
|
||||
"repo.wiki": "none",
|
||||
"repo.pulls": "none",
|
||||
"repo.releases": "none",
|
||||
"repo.projects": "none",
|
||||
"repo.ext_wiki": "none"
|
||||
},
|
||||
"team_repositories": [
|
||||
"coulomb/rapp-openbao",
|
||||
"coulomb/rapp-policy-nexus",
|
||||
"coulomb/rapp-postgres",
|
||||
"coulomb/rapp-sbom-nexus",
|
||||
"coulomb/rapp-telemetry",
|
||||
"coulomb/rapp-tenant-engine",
|
||||
"coulomb/rapp-user-engine"
|
||||
],
|
||||
"token_scopes": [
|
||||
"read:repository"
|
||||
],
|
||||
"token_name_prefix": "reuse-surface-source-read-",
|
||||
"reuse_other_workload_token": false,
|
||||
"fail_on_differing_existing_identity_or_team": true,
|
||||
"enforcement": "Verify complete team membership and all effective user repository grants; no other team, collaborator or organisation grants may provide extra private read access."
|
||||
},
|
||||
"delivery": {
|
||||
"kv_mount": "platform",
|
||||
"kv_path": "platform/workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"field": "FORGEJO_SOURCE_TOKEN",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"eso_role": "reuse-surface-runtime-eso",
|
||||
"eso_identity": "reuse/reuse-surface-eso",
|
||||
"audience": "openbao",
|
||||
"ttl": "15m",
|
||||
"cluster_secret_store": "openbao-reuse",
|
||||
"store_namespace_condition": [
|
||||
"reuse"
|
||||
],
|
||||
"external_secret": "reuse/reuse-surface-runtime",
|
||||
"target_secret": "reuse/reuse-surface-env",
|
||||
"existing_consumption": "Deployment reuse/reuse-surface envFrom secretRef reuse-surface-env; environment variable available only after controlled rollout. Existing image and resource requests retained.",
|
||||
"policy_delta": {
|
||||
"file": "railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
"add_exact_stanza": "path \"platform/data/workloads/reuse/reuse-surface/forgejo-source-read\" { capabilities = [\"read\"] }",
|
||||
"retain_existing_paths": true,
|
||||
"do_not_change": "Caller-facing workload-kv-read-reuse-surface-runtime policy or writer OIDC role; those remain unable to read this new path."
|
||||
},
|
||||
"external_secret_data_delta": {
|
||||
"secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"remoteRef": {
|
||||
"key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"property": "FORGEJO_SOURCE_TOKEN"
|
||||
}
|
||||
},
|
||||
"delivery_design_reason": "Uses the existing namespace-limited ESO store and identity plus the existing envFrom interface. Separate new KV path keeps scoped writer callers from also acquiring private-source access. No new static ESO token or new store is needed.",
|
||||
"native_source_auth": {
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"auth_header": "Authorization",
|
||||
"header_behavior": "Native federation _auth_headers prefixes a bare token with Bearer; owner must verify accepted Forgejo raw API route with this actual native implementation."
|
||||
},
|
||||
"rollout_image": "Pending full consumer fix reviewed image digest; same-image rollout of current consumer is prohibited."
|
||||
},
|
||||
"consumer_gate": "Reader must not be delivered until reuse-surface owner supplies verified image digest containing BOTH authenticated redirect rejection/HTTPS enforcement AND runtime-controlled exact URL allowlist checked before credential lookup. Apps owner must deploy REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST mapping the reader environment to exactly fourteen consented pinned index/entry URLs and inventory existing authenticated sources for migration. Current image and redirect-only fix are not approved. Native owner helper must verify all seven pinned indexes and entry hashes after rollout.",
|
||||
"approval": "No approval recorded. External supervisor approval receipt must bind exactly CCR, script, contract and ESO policy bytes; CCR must be approved. Additional forge/apps/reuse owner reviews and rollout approval remain required.",
|
||||
"metadata_consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
"preparation_sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99",
|
||||
"recovery": "On ambiguous mutation stop. Reconcile complete Forgejo identity/team/repository/PAT metadata and exact KV field through suppressed helper before retry. Token without KV or reverse refuses automatically; revoke identified dedicated PAT through attended Forgejo owner recovery before retry. Never remint on observation timeout. KV CAS=0 prevents overwrites. Do not delete shared runtime Secret.",
|
||||
"consumer_preparation": {
|
||||
"owner": "reuse-surface",
|
||||
"allowlist_artifact": "/tmp/cust81-reuse-reader-owner/docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json",
|
||||
"allowlist_sha256": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
"federation_py_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
"integration_patch_sha256": "da9634481ce80658dfce815d3539ba023234938dc76163dfd93d7677be95ed51",
|
||||
"review_state": "changes_requested for current consumer; preparation patch alone is not deployed-image evidence"
|
||||
},
|
||||
"pagination_contract": "All list endpoints page until observed empty, max20pages; reject duplicate/missing IDs, reconcile X-Total-Count when supplied, reject changed/mismatched counts and bound exhaustion. Never infer completion from requestedlimit100 versus server-capped50.",
|
||||
"effective_grants_contract": "Forgejo11.0.3 /user/repos?order_by=id with Sudo reader uses OwnerID=reader and Collaborate=None; SearchRepositoryCondition defaults collaborations true and includes direct UserAccessRepoCond plus UserOrgTeamRepoCond. It is not owned-only despite Swagger summary. Independent deployed-version compatibility and live inventory proof pending.",
|
||||
"r1_review_failure": {
|
||||
"patch_sha256": "a94547b419b87e38dc178b02d1d31af61cc11a71e3734052a190167668db8e30",
|
||||
"confirmed_defect": "R1 len(batch)<100 stopped after server-capped50, omitting later grants",
|
||||
"corrected_preliminary_claim": "Owned-only endpoint claim disproved by Forgejo11.0.3 model defaults; no endpoint replacement based on false premise",
|
||||
"same_proposal": "CCR-2026-0046; revision2 is refinement, not a new successful trial"
|
||||
},
|
||||
"api_source_evidence": [
|
||||
{
|
||||
"upstream_path": "models/repo/repo/list.go",
|
||||
"url": "https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/models/repo/repo/list.go",
|
||||
"sha256": "bb7833badf0fe7f6a2c800c2f301cb846b5968ce3f9e9bd29c3f1677db42acd9",
|
||||
"local_cache": "/tmp/cust81-forge-contract-source/source-models_repo_repo_list.go"
|
||||
},
|
||||
{
|
||||
"upstream_path": "routers/api/v1/utils/page.go",
|
||||
"url": "https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/routers/api/v1/utils/page.go",
|
||||
"sha256": "1d286f60b5571513fb5a26a9243c22eae65f05d7be32dfe5d0f0b15d20550954",
|
||||
"local_cache": "/tmp/cust81-forge-contract-source/source-routers_api_v1_utils_page.go"
|
||||
},
|
||||
{
|
||||
"upstream_path": "services/convert/utils.go",
|
||||
"url": "https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/services/convert/utils.go",
|
||||
"sha256": "2689ffc579ea1df69457fbe84410fef101ae4a9580f0e56dcc3113a2988626b3",
|
||||
"local_cache": "/tmp/cust81-forge-contract-source/source-services_convert_utils.go"
|
||||
},
|
||||
{
|
||||
"upstream_path": "modules/setting/api.go",
|
||||
"url": "https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/modules/setting/api.go",
|
||||
"sha256": "1f640ed884afaa6454c4bf427eccd2d6bb9a092fe37ac1aedfdc5eb85de4f7ba",
|
||||
"local_cache": "/tmp/cust81-forge-contract-source/source-modules_setting_api.go"
|
||||
},
|
||||
{
|
||||
"upstream_path": "routers/api/v1/user/repo.go",
|
||||
"url": "https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/routers/api/v1/user/repo.go",
|
||||
"sha256": "adee62e6671aadd95b5013a64dbb690f2d81a0b9b4de0350c70b4153074ca6ec",
|
||||
"local_cache": "/tmp/cust81-forge-contract-source/source-routers_api_v1_user_repo.go"
|
||||
}
|
||||
],
|
||||
"rollback_helper": {
|
||||
"path": "scripts/revoke-reuse-surface-forgejo-source.py",
|
||||
"scope": "Separate approval binds exact PAT id/name and artifact bytes; Basic-auth revocation after dedicated-user memory-only password reset. Preserve KV/sharedruntime; no remint. Reconcile uncertain DELETE by inventory absence."
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"id": "RPF-WP-0058-PLATFORM-OWNER-REVIEW-R1",
|
||||
"owner_repo": "railiance-platform",
|
||||
"reviewer": "platform-reader-owner delegated worker",
|
||||
"basis": [
|
||||
"INTENT.md shared stateful service custody, Q1 approval/delegated apply/revocation, stable delivery interface",
|
||||
"AGENTS.md credential-change owner review and founder-gated attended provisioning",
|
||||
"No own supervision record exists; supervised preparation only"
|
||||
],
|
||||
"disposition": "prepared_with_conditions_not_execution_approval",
|
||||
"scope": "Only platform custody/CCR/policy/bootstrap; other repo reviews not authored",
|
||||
"findings": [
|
||||
"Separate exact KV path and exact data-read ESO delta preserve writer-only caller boundary.",
|
||||
"No all-repo Forgejo team; preflight rejects different existing user/team, unexpected team repos/members, other teams/orgs/private grants, orphan PAT/KV, and unverified PAT/KV binding.",
|
||||
"Quiet create-only bootstrap with per-user lane lock, durable fsynced intents/outcomes, CAS=0 KV; uncertainty is reconciled before retry, never blindly reminted.",
|
||||
"Full consumer redirect/HTTPS plus authenticated URL allowlist image and exact config mandatory before credential delivery.",
|
||||
"Approval receipt is locally bound revision evidence, not authenticated authority; attended founder/platform lane remains authorization source."
|
||||
],
|
||||
"validation": {
|
||||
"targeted_tests": 17,
|
||||
"result": "passed",
|
||||
"ccr_validator": "passed with expected unconfirmed ESO binding/apply blocked warning",
|
||||
"diff_check": "passed"
|
||||
},
|
||||
"pending": [
|
||||
"Allocation recheck against owner primary before integration",
|
||||
"Independent Forgejo/apps/reuse owner reviews and actual founder credential provisioning decision",
|
||||
"Live ESO claims and namespace condition confirmation; no token read or live mutations performed",
|
||||
"Forgejo deployed API metadata contract support proof; sudo inventory and token_last_eight missing metadata fail closed",
|
||||
"Apps fixedimage digest/config/migration/dryrun/headroom rollout approval and receipt",
|
||||
"Native seven index+entry verification and final hosted70/70 audit"
|
||||
],
|
||||
"execution_approved": false,
|
||||
"live_changes_performed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"id": "RPF-WP-0058-PLATFORM-OWNER-REVIEW-R2",
|
||||
"owner_repo": "railiance-platform",
|
||||
"reviewer": "platform-reader-owner delegated worker",
|
||||
"basis": [
|
||||
"INTENT.md shared stateful service custody, Q1 approval/delegated apply/revocation, stable delivery interface",
|
||||
"AGENTS.md credential-change owner review and founder-gated attended provisioning",
|
||||
"No own supervision record exists; supervised preparation only"
|
||||
],
|
||||
"disposition": "approved",
|
||||
"scope": "Platform custody PREPARATION artifact revision2 only; not founder credential/production authority or another repo review",
|
||||
"findings": [
|
||||
"Separate exact KV path and exact data-read ESO delta preserve writer-only caller boundary.",
|
||||
"No all-repo Forgejo team; preflight rejects different existing user/team, unexpected team repos/members, other teams/orgs/private grants, orphan PAT/KV, and unverified PAT/KV binding.",
|
||||
"Quiet create-only bootstrap with per-user lane lock, durable fsynced intents/outcomes, CAS=0 KV; uncertainty is reconciled before retry, never blindly reminted.",
|
||||
"Full consumer redirect/HTTPS plus authenticated URL allowlist image and exact config mandatory before credential delivery.",
|
||||
"Approval receipt is locally bound revision evidence, not authenticated authority; attended founder/platform lane remains authorization source.",
|
||||
"Forgejo version source disproves preliminary owned-only /user/repos claim; unchanged endpoint with id sort includes direct and team grants. R2 pages until observed empty, refuses missing/duplicate IDs and inconsistent total counts."
|
||||
],
|
||||
"validation": {
|
||||
"targeted_tests": 28,
|
||||
"result": "passed",
|
||||
"ccr_validator": "passed with expected unconfirmed ESO binding/apply blocked warning",
|
||||
"diff_check": "passed"
|
||||
},
|
||||
"pending": [
|
||||
"Allocation recheck against owner primary before integration",
|
||||
"Independent Forgejo/apps/reuse owner reviews and actual founder credential provisioning decision",
|
||||
"Live ESO claims and namespace condition confirmation; no token read or live mutations performed",
|
||||
"Forgejo deployed API metadata contract support proof; sudo inventory and token_last_eight missing metadata fail closed",
|
||||
"Apps fixedimage digest/config/migration/dryrun/headroom rollout approval and receipt",
|
||||
"Native seven index+entry verification and final hosted70/70 audit",
|
||||
"Separate explicit exact-token recovery approval and manual rotation due-date task/receipt before activation."
|
||||
],
|
||||
"execution_approved": false,
|
||||
"live_changes_performed": false,
|
||||
"revision": 2,
|
||||
"decision": "approved",
|
||||
"work_item": "RPF-WP-0058-T01",
|
||||
"reviewed_artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "90522912fb78fed72e625382fb7114a0e9aafe76d59e36bffecab83c5bb7d643",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "abf60a754596baa1610d23b1fd367385b0222ac8aeaebba0f2cfb7ec4c62055b",
|
||||
"scripts/revoke-reuse-surface-forgejo-source.py": "f78d1b26fbff93c6cc481a896d9fb9b9ce93049b8b3267fa41a64bc57dadb162",
|
||||
"tests/test_reuse_surface_forgejo_source_provision.py": "0d06fd22596a0f17ecd8dd5ff6a12bb1b1782258d00126ad468661099d7d298b",
|
||||
"tests/test_reuse_surface_forgejo_source_recovery.py": "08247129b220c198d8f7d1322ce63956203b9ffcaa0342e52990bb6520fd138d",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "2bb72671e0a5f230497ab8ef2967a20ee68d1f2cf081f07c84f6db3442e18d8b",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/README.md": "dcc8879d96b85b21b52c6cac9d7a58db78169ed903490ef11561bf8d6aa507e5",
|
||||
"workplans/RPF-WP-0058-reuse-private-source-reader.md": "9aeb0fbbb5359b7ba9afb4f5f7f2c55f84dade0d6d95fe98b15901e656aed5b5",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/apps-externalsecret-data-item.draft.yaml": "e582a7d2eeb68668858ec19a6a32b8b816a4850b5ad1757f7686330b141578b2"
|
||||
},
|
||||
"supersedes_review_artifact_sha256": "0a9746f65bb79982632accd9e20a2a632eafc5ee5410f4eddeadd625e73ca0bd",
|
||||
"refinement_of_same_proposal": "CCR-2026-0046 R1 confirmed capped-page bug retained; R2 fixes pagination, not a new trial"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
path "platform/data/workloads/reuse/reuse-surface/runtime-secrets" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/lookup-self" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/revoke-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
|
||||
# CCR-2026-0046 proposed; exact extra data path, no metadata/list grants.
|
||||
path "platform/data/workloads/reuse/reuse-surface/forgejo-source-read" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Silent attended CCR-2026-0046 bootstrap; never runs without exact approval.
|
||||
|
||||
Only this seven-repository identity/PAT/KV path is mutable. No policy, ESO,
|
||||
consumer rollout, or registration mutations occur here. A receipt is evidence,
|
||||
not authorization. Uncertain requests are recorded and reconciled, never retried
|
||||
in the same execution; orphan PAT drift requires attended recovery.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import base64
|
||||
import datetime as dt
|
||||
import fcntl
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CCR = 'CCR-2026-0046'
|
||||
CCR_FILE = 'credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml'
|
||||
PLAN_FILE = 'docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json'
|
||||
SCRIPT_FILE = 'scripts/provision-reuse-surface-forgejo-source.py'
|
||||
API = 'https://forgejo.coulomb.social/api/v1'
|
||||
USER = 'reuse-surface-source'
|
||||
TEAM = 'reuse-surface-source-readers'
|
||||
ORG = 'coulomb'
|
||||
KV = 'platform/workloads/reuse/reuse-surface/forgejo-source-read'
|
||||
FIELD = 'FORGEJO_SOURCE_TOKEN'
|
||||
PREFIX = 'reuse-surface-source-read-'
|
||||
REPOS = frozenset('rapp-openbao rapp-policy-nexus rapp-postgres rapp-sbom-nexus rapp-telemetry rapp-tenant-engine rapp-user-engine'.split())
|
||||
SCOPES = ['read:repository']
|
||||
UNITS = {'repo.code': 'read', **{x: 'none' for x in ('repo.actions', 'repo.packages', 'repo.issues', 'repo.ext_issues', 'repo.wiki', 'repo.pulls', 'repo.releases', 'repo.projects', 'repo.ext_wiki')}}
|
||||
|
||||
class Refused(Exception):
|
||||
"""Messages must be fixed non-secret codes, never provider text."""
|
||||
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, *args, **kwargs):
|
||||
raise Refused('redirect_refused')
|
||||
|
||||
class Journal:
|
||||
def __init__(self, path):
|
||||
flags = os.O_RDWR | os.O_CREAT | os.O_APPEND | os.O_NOFOLLOW
|
||||
self.fd = os.open(path, flags, 0o600)
|
||||
if os.fstat(self.fd).st_mode & 0o077:
|
||||
os.close(self.fd)
|
||||
raise Refused('receipt_permissions')
|
||||
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
self.attempt = secrets.token_hex(12)
|
||||
def put(self, event, **fields):
|
||||
# Only locally authored operation tags, scalar status/IDs are accepted.
|
||||
allowed = {'operation', 'status', 'http_status', 'token_id', 'team_id', 'outcome', 'error_code'}
|
||||
if set(fields) - allowed:
|
||||
raise Refused('receipt_fields')
|
||||
record = {'at': dt.datetime.now(dt.timezone.utc).isoformat(), 'ccr': CCR,
|
||||
'attempt': self.attempt, 'event': event, **fields}
|
||||
data = (json.dumps(record, sort_keys=True) + '\n').encode()
|
||||
while data:
|
||||
written = os.write(self.fd, data)
|
||||
if written <= 0:
|
||||
raise Refused('receipt_write_failed')
|
||||
data = data[written:]
|
||||
os.fsync(self.fd)
|
||||
def close(self):
|
||||
os.close(self.fd)
|
||||
|
||||
class Client:
|
||||
def __init__(self, admin, journal):
|
||||
self.admin, self.journal = admin, journal
|
||||
self.opener = urllib.request.build_opener(NoRedirect())
|
||||
def request(self, method, path, payload=None, expected=(200,), *, token=None, sudo=False, basic=None, operation=None):
|
||||
if not path.startswith('/') or '://' in path:
|
||||
raise Refused('invalid_api_path')
|
||||
headers = {'Authorization': basic or 'token ' + (self.admin if token is None else token), 'Accept': 'application/json'}
|
||||
if sudo:
|
||||
headers['Sudo'] = USER
|
||||
if payload is not None:
|
||||
headers['Content-Type'] = 'application/json'
|
||||
if method != 'GET':
|
||||
if not operation:
|
||||
raise Refused('mutation_without_intent')
|
||||
self.journal.put('intent', operation=operation)
|
||||
req = urllib.request.Request(API + path, data=None if payload is None else json.dumps(payload).encode(), headers=headers, method=method)
|
||||
self.last_total_count = None
|
||||
try:
|
||||
with self.opener.open(req, timeout=30) as response:
|
||||
status, body = response.status, response.read(2_000_001)
|
||||
raw_total = response.headers.get('X-Total-Count')
|
||||
if raw_total is not None:
|
||||
if not re.fullmatch(r'[0-9]+', raw_total):
|
||||
raise Refused('invalid_total_count')
|
||||
self.last_total_count = int(raw_total)
|
||||
except urllib.error.HTTPError as error:
|
||||
status, body = error.code, b''
|
||||
except Exception:
|
||||
if operation:
|
||||
self.journal.put('outcome', operation=operation, status='ambiguous')
|
||||
raise Refused('request_observation_failed') from None
|
||||
if operation:
|
||||
self.journal.put('outcome', operation=operation, status='observed', http_status=status)
|
||||
if status not in expected:
|
||||
raise Refused('unexpected_http_status')
|
||||
if len(body) > 2_000_000:
|
||||
raise Refused('response_bound_exceeded')
|
||||
if not body:
|
||||
return status, None
|
||||
try:
|
||||
return status, json.loads(body)
|
||||
except Exception:
|
||||
raise Refused('invalid_json') from None
|
||||
def pages(self, path, *, sudo=False):
|
||||
"""Page until observed empty; requested size may be server-clamped.
|
||||
|
||||
Forgejo11.0.3 default MaxResponseItems=50, so a response shorter than
|
||||
our requested100 proves nothing. Detect unstable/duplicate IDs and
|
||||
reconcile X-Total-Count whenever available; exceeding bound refuses.
|
||||
"""
|
||||
items, seen, expected_total = [], set(), None
|
||||
for page in range(1, 21):
|
||||
separator = '&' if '?' in path else '?'
|
||||
_, batch = self.request('GET', path + separator + f'limit=100&page={page}', sudo=sudo)
|
||||
if not isinstance(batch, list) or any(not isinstance(x, dict) or type(x.get('id')) is not int or x['id'] <= 0 for x in batch):
|
||||
raise Refused('invalid_list')
|
||||
observed_total = getattr(self, 'last_total_count', None)
|
||||
if observed_total is not None:
|
||||
if expected_total is not None and observed_total != expected_total:
|
||||
raise Refused('unstable_total_count')
|
||||
expected_total = observed_total
|
||||
if not batch:
|
||||
if expected_total is not None and len(items) != expected_total:
|
||||
raise Refused('total_count_mismatch')
|
||||
return items
|
||||
for item in batch:
|
||||
if item['id'] in seen:
|
||||
raise Refused('duplicate_page_id')
|
||||
seen.add(item['id'])
|
||||
items.extend(batch)
|
||||
if expected_total is not None and len(items) > expected_total:
|
||||
raise Refused('total_count_mismatch')
|
||||
raise Refused('pagination_bound_exceeded')
|
||||
|
||||
class Bao:
|
||||
@staticmethod
|
||||
def run(args, stdin=None):
|
||||
try:
|
||||
return subprocess.run(['bao', *args], input=stdin, capture_output=True, timeout=60, check=False, cwd=ROOT)
|
||||
except Exception:
|
||||
raise Refused('bao_observation_failed') from None
|
||||
def get(self):
|
||||
result = self.run(['kv', 'get', '-format=json', KV])
|
||||
if result.returncode != 0:
|
||||
# Distinguish an actual not-found response from denied/unavailable.
|
||||
# Never include captured raw errors in any evidence.
|
||||
if re.search(rb'Code:\s*404\b', result.stderr or b'') or b'No value found at ' in (result.stderr or b''):
|
||||
return None
|
||||
raise Refused('bao_preflight_failed')
|
||||
try:
|
||||
data = json.loads(result.stdout)['data']['data']
|
||||
except Exception:
|
||||
raise Refused('bao_invalid_record') from None
|
||||
if set(data) != {FIELD, 'API_USER', 'API_BASE_URL', 'TOKEN_SCOPES', 'GENERATED_AT', 'CCR_ID'} or data['API_USER'] != USER or data['API_BASE_URL'] != 'https://forgejo.coulomb.social' or data['TOKEN_SCOPES'] != 'read:repository' or data['CCR_ID'] != CCR or not isinstance(data[FIELD], str) or not data[FIELD]:
|
||||
raise Refused('bao_record_conflict')
|
||||
return data
|
||||
def put(self, token, journal):
|
||||
journal.put('intent', operation='kv_create')
|
||||
timestamp = dt.datetime.now(dt.timezone.utc).isoformat()
|
||||
try:
|
||||
result = self.run(['kv', 'put', '-cas=0', KV, FIELD + '=-', 'API_USER=' + USER,
|
||||
'API_BASE_URL=https://forgejo.coulomb.social', 'TOKEN_SCOPES=read:repository',
|
||||
'GENERATED_AT=' + timestamp, 'CCR_ID=' + CCR], token.encode())
|
||||
except Refused:
|
||||
journal.put('outcome', operation='kv_create', status='ambiguous')
|
||||
raise
|
||||
journal.put('outcome', operation='kv_create', status='observed' if result.returncode == 0 else 'ambiguous')
|
||||
# Readback reconciles even a response failure after successful write.
|
||||
stored = self.get()
|
||||
if stored is None or not secrets.compare_digest(stored[FIELD], token):
|
||||
raise Refused('bao_write_requires_reconciliation')
|
||||
journal.put('verified', operation='kv_create', status='matched')
|
||||
|
||||
|
||||
def valid_user(user):
|
||||
return isinstance(user, dict) and user.get('login') == USER and user.get('restricted') is True and user.get('is_admin') is False and user.get('visibility') == 'private'
|
||||
|
||||
def valid_team(team):
|
||||
return isinstance(team, dict) and isinstance(team.get('id'), int) and team.get('name') == TEAM and team.get('permission') == 'read' and team.get('includes_all_repositories') is False and team.get('can_create_org_repo') is False and team.get('units_map') == UNITS
|
||||
|
||||
def repo_names(items):
|
||||
names = set()
|
||||
for repo in items:
|
||||
if repo.get('owner', {}).get('login') != ORG or repo.get('name') not in REPOS:
|
||||
raise Refused('unexpected_team_repository')
|
||||
names.add(repo['name'])
|
||||
if len(names) != len(items):
|
||||
raise Refused('duplicate_repository')
|
||||
return names
|
||||
|
||||
def inspect(client, bao):
|
||||
"""Read-only preflight before mutations.
|
||||
|
||||
Forgejo11.0.3 ListMyRepos uses OwnerID plus Collaborate=None; model
|
||||
ValueOrDefault(true) includes direct access and team grants (not only owned).
|
||||
Deployed-server API compatibility and live scope proof remain separate gates.
|
||||
"""
|
||||
for repo in sorted(REPOS):
|
||||
_, meta = client.request('GET', f'/repos/{ORG}/{repo}')
|
||||
if not isinstance(meta, dict) or meta.get('private') is not True or meta.get('full_name') != ORG + '/' + repo:
|
||||
raise Refused('source_repository_conflict')
|
||||
_, probe = client.request('GET', '/repos/coulomb/rapp-core-hub')
|
||||
if not isinstance(probe, dict) or probe.get('private') is not True:
|
||||
raise Refused('negative_probe_not_private')
|
||||
_, user = client.request('GET', '/users/' + USER, expected=(200, 404))
|
||||
if user is not None and not valid_user(user):
|
||||
raise Refused('user_conflict')
|
||||
teams = [x for x in client.pages(f'/orgs/{ORG}/teams') if x.get('name') == TEAM]
|
||||
if len(teams) > 1:
|
||||
raise Refused('duplicate_team')
|
||||
team = teams[0] if teams else None
|
||||
members, repos = [], set()
|
||||
if team is not None:
|
||||
if not valid_team(team):
|
||||
raise Refused('team_conflict')
|
||||
members = client.pages('/teams/' + str(team['id']) + '/members')
|
||||
if len(members) > 1 or any(x.get('login') != USER for x in members):
|
||||
raise Refused('team_members_conflict')
|
||||
repos = repo_names(client.pages('/teams/' + str(team['id']) + '/repos'))
|
||||
tokens = []
|
||||
if user is not None:
|
||||
orgs = client.pages('/user/orgs', sudo=True)
|
||||
if any(x.get('username') != ORG for x in orgs):
|
||||
raise Refused('extra_organization_affiliation')
|
||||
grants = client.pages('/user/teams', sudo=True)
|
||||
if any(x.get('name') != TEAM or x.get('organization', {}).get('username') != ORG for x in grants):
|
||||
raise Refused('extra_team_grant')
|
||||
for meta in client.pages('/user/repos?order_by=id', sudo=True):
|
||||
if meta.get('private') is True and meta.get('full_name') not in {ORG + '/' + x for x in REPOS}:
|
||||
raise Refused('extra_private_repository_grant')
|
||||
if meta.get('owner', {}).get('login') == USER:
|
||||
raise Refused('service_user_owned_repository')
|
||||
tokens = client.pages('/users/' + USER + '/tokens')
|
||||
if len(tokens) > 1 or any(not str(x.get('name', '')).startswith(PREFIX) or x.get('scopes') != SCOPES or not isinstance(x.get('id'), int) for x in tokens):
|
||||
raise Refused('token_conflict')
|
||||
stored = bao.get()
|
||||
if bool(tokens) != bool(stored):
|
||||
raise Refused('orphan_token_or_kv_requires_recovery')
|
||||
if tokens and (not isinstance(tokens[0].get('token_last_eight'), str) or len(tokens[0]['token_last_eight']) != 8 or not secrets.compare_digest(tokens[0]['token_last_eight'], stored[FIELD][-8:])):
|
||||
raise Refused('pat_kv_binding_unverifiable')
|
||||
if tokens and (not team or repos != REPOS or len(members) != 1):
|
||||
raise Refused('active_lane_grant_drift')
|
||||
return user, team, repos, members, tokens, stored
|
||||
|
||||
def verify(client, source_token, plan):
|
||||
for source in plan['sources']:
|
||||
_, repo = client.request('GET', '/repos/' + ORG + '/' + source['repo'], token=source_token)
|
||||
if repo.get('permissions') != {'admin': False, 'push': False, 'pull': True}:
|
||||
raise Refused('effective_permission_not_read')
|
||||
# Exact consented bytes fetched quietly; raw output is never retained.
|
||||
path = f"/repos/{ORG}/{source['repo']}/contents/registry/indexes/capabilities.yaml?ref={source['consented_commit']}"
|
||||
_, content = client.request('GET', path, token=source_token)
|
||||
try:
|
||||
raw = base64.b64decode(content['content'], validate=False)
|
||||
except Exception:
|
||||
raise Refused('invalid_index_encoding') from None
|
||||
if hashlib.sha256(raw).hexdigest() != source['index_sha256']:
|
||||
raise Refused('index_digest_mismatch')
|
||||
client.request('GET', f"/repos/{ORG}/{source['repo']}/actions/secrets?limit=1", token=source_token, expected=(403, 404))
|
||||
client.request('GET', '/admin/users', token=source_token, expected=(403, 404))
|
||||
# A known private excluded source is selected by reviewed contract.
|
||||
probe = plan['negative_private_probe']
|
||||
_, meta = client.request('GET', '/repos/' + ORG + '/' + probe)
|
||||
if not isinstance(meta, dict) or meta.get('private') is not True:
|
||||
raise Refused('negative_probe_not_private')
|
||||
client.request('GET', '/repos/' + ORG + '/' + probe, token=source_token, expected=(403, 404))
|
||||
client.journal.put('verified', operation='reader_scope', status='seven_exact_no_extra_private_grants')
|
||||
|
||||
def provision(client, bao, plan):
|
||||
user, team, repos, members, tokens, stored = inspect(client, bao)
|
||||
if tokens:
|
||||
verify(client, stored[FIELD], plan)
|
||||
client.journal.put('terminal', outcome='verified_noop')
|
||||
return 'noop'
|
||||
if user is None:
|
||||
_, user = client.request('POST', '/admin/users', {'username': USER, 'email': USER + '@coulomb.social', 'full_name': 'Reuse Surface seven-source reader', 'password': secrets.token_urlsafe(48), 'restricted': True, 'must_change_password': False, 'send_notify': False, 'visibility': 'private'}, expected=(201,), operation='user_create')
|
||||
if not valid_user(user):
|
||||
raise Refused('created_user_contract_failed')
|
||||
if team is None:
|
||||
_, team = client.request('POST', f'/orgs/{ORG}/teams', {'name': TEAM, 'description': CCR + ' exact seven consented source repos', 'permission': 'read', 'includes_all_repositories': False, 'can_create_org_repo': False, 'units': ['repo.code'], 'units_map': UNITS}, expected=(201,), operation='team_create')
|
||||
if not valid_team(team):
|
||||
raise Refused('created_team_contract_failed')
|
||||
team_id = team['id']
|
||||
for repo in sorted(REPOS - repos):
|
||||
client.request('PUT', f'/teams/{team_id}/repos/{ORG}/{repo}', expected=(204,), operation='team_repo_' + repo)
|
||||
if not members:
|
||||
client.request('PUT', f'/teams/{team_id}/members/{USER}', expected=(204,), operation='team_member_add')
|
||||
# Recheck complete grants before creating any PAT; refuse implicit grants.
|
||||
_, _, actual_repos, actual_members, _, _ = inspect(client, bao)
|
||||
if actual_repos != REPOS or len(actual_members) != 1:
|
||||
raise Refused('incomplete_team_scope')
|
||||
password = secrets.token_urlsafe(48)
|
||||
client.request('PATCH', '/admin/users/' + USER, {'password': password, 'restricted': True, 'admin': False, 'allow_create_organization': False, 'allow_git_hook': False, 'allow_import_local': False, 'max_repo_creation': 0, 'must_change_password': False, 'active': True, 'visibility': 'private'}, operation='service_password_randomize')
|
||||
basic = 'Basic ' + base64.b64encode((USER + ':' + password).encode()).decode()
|
||||
_, record = client.request('POST', '/users/' + USER + '/tokens', {'name': PREFIX + client.journal.attempt, 'scopes': SCOPES}, expected=(201,), basic=basic, operation='pat_create')
|
||||
if not isinstance(record, dict) or not isinstance(record.get('id'), int) or record.get('scopes') != SCOPES or not isinstance(record.get('sha1'), str) or not record['sha1']:
|
||||
raise Refused('created_pat_requires_recovery')
|
||||
client.journal.put('identified', operation='pat_create', token_id=record['id'], team_id=team_id)
|
||||
token = record['sha1']
|
||||
verify(client, token, plan)
|
||||
bao.put(token, client.journal)
|
||||
# Repeat complete grant inventory after custody; verify exactly one PAT.
|
||||
inspect(client, bao)
|
||||
client.journal.put('terminal', outcome='provisioned_verified_delivery_pending')
|
||||
return 'minted'
|
||||
|
||||
def approval(path):
|
||||
try:
|
||||
receipt = json.loads(Path(path).read_text())
|
||||
ccr = yaml.safe_load((ROOT / CCR_FILE).read_text())
|
||||
if receipt['ccr_id'] != CCR or receipt['decision'] != 'approved' or not receipt['supervisor'] or not receipt['decision_ref'] or ccr['status'] != 'approved':
|
||||
raise Refused('approval_missing')
|
||||
files = {CCR_FILE, PLAN_FILE, SCRIPT_FILE, 'openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl'}
|
||||
if set(receipt['approved_artifacts']) != files:
|
||||
raise Refused('approval_artifact_set')
|
||||
for rel in files:
|
||||
if hashlib.sha256((ROOT / rel).read_bytes()).hexdigest() != receipt['approved_artifacts'][rel]:
|
||||
raise Refused('approval_revision_mismatch')
|
||||
plan = json.loads((ROOT / PLAN_FILE).read_text())
|
||||
if {x['repo'] for x in plan['sources']} != REPOS or len(plan['sources']) != 7 or plan['negative_private_probe'] != 'rapp-core-hub' or any(not re.fullmatch(r'[a-f0-9]{40}', x['consented_commit']) or not re.fullmatch(r'[a-f0-9]{64}', x['index_sha256']) for x in plan['sources']):
|
||||
raise Refused('plan_scope_conflict')
|
||||
return plan
|
||||
except Refused:
|
||||
raise
|
||||
except Exception:
|
||||
raise Refused('approval_invalid') from None
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--approval', required=True)
|
||||
parser.add_argument('--receipt', required=True)
|
||||
parser.add_argument('--inner', action='store_true', help=argparse.SUPPRESS)
|
||||
args = parser.parse_args(argv)
|
||||
journal = None
|
||||
lane_lock = None
|
||||
try:
|
||||
plan = approval(args.approval)
|
||||
if not args.inner:
|
||||
# Governed OpenBao attended lane surrounds this command. Warden
|
||||
# supplies only Forgejo API_TOKEN to inner child, not via argv/files.
|
||||
result = subprocess.run(['warden', 'access', 'forgejo-admin-api-token', '--field', 'API_TOKEN', '--exec', '--', sys.executable, str(ROOT / SCRIPT_FILE), '--inner', '--approval', str(Path(args.approval).resolve()), '--receipt', str(Path(args.receipt).resolve())], capture_output=True, timeout=900, check=False, cwd=ROOT)
|
||||
return 0 if result.returncode == 0 else 1
|
||||
lane_lock = os.open('/tmp/' + CCR + '-reuse-reader-' + str(os.getuid()) + '.lock', os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600)
|
||||
if os.fstat(lane_lock).st_uid != os.getuid() or os.fstat(lane_lock).st_mode & 0o077:
|
||||
raise Refused('lane_lock_owner_or_permissions')
|
||||
fcntl.flock(lane_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
journal = Journal(args.receipt)
|
||||
admin = os.environ.get('API_TOKEN')
|
||||
if not admin:
|
||||
raise Refused('admin_route_missing')
|
||||
provision(Client(admin, journal), Bao(), plan)
|
||||
return 0
|
||||
except Exception as error:
|
||||
if journal:
|
||||
journal.put('terminal', outcome='stopped_reconcile_before_retry', error_code=str(error) if isinstance(error, Refused) else 'internal_failure')
|
||||
return 1
|
||||
finally:
|
||||
if journal:
|
||||
journal.close()
|
||||
if lane_lock is not None:
|
||||
os.close(lane_lock)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
result = main()
|
||||
except Exception:
|
||||
result = 1
|
||||
raise SystemExit(result)
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Silent separately approved exact-PAT revocation; preserve KV and delivery.
|
||||
|
||||
Forgejo11.0.3 DELETE token requires Basic auth. Reset only the dedicated user
|
||||
password to process-memory random bytes, revoke the reviewed token and reconcile
|
||||
inventory absence. Never remint, rotate/write KV, or touch another identity.
|
||||
"""
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SCRIPT = 'scripts/revoke-reuse-surface-forgejo-source.py'
|
||||
spec = importlib.util.spec_from_file_location('reuse_reader_bootstrap', ROOT / 'scripts/provision-reuse-surface-forgejo-source.py')
|
||||
p = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(p)
|
||||
|
||||
|
||||
def approved_target(path):
|
||||
try:
|
||||
record = json.loads(Path(path).read_text())
|
||||
expected_files = {SCRIPT, p.SCRIPT_FILE, p.CCR_FILE, p.PLAN_FILE}
|
||||
if record['ccr_id'] != p.CCR or record['decision'] != 'approved' or not record['supervisor'] or not record['decision_ref'] or record['action'] != 'revoke_exact_pat' or set(record['approved_artifacts']) != expected_files:
|
||||
raise p.Refused('recovery_approval_invalid')
|
||||
for rel in expected_files:
|
||||
if hashlib.sha256((ROOT / rel).read_bytes()).hexdigest() != record['approved_artifacts'][rel]:
|
||||
raise p.Refused('recovery_approved_revision_mismatch')
|
||||
target = record['target']
|
||||
if set(target) != {'id', 'name'} or type(target['id']) is not int or target['id'] <= 0 or not isinstance(target['name'], str) or not target['name'].startswith(p.PREFIX) or len(target['name']) > 100:
|
||||
raise p.Refused('recovery_target_invalid')
|
||||
return target
|
||||
except p.Refused:
|
||||
raise
|
||||
except Exception:
|
||||
raise p.Refused('recovery_approval_invalid') from None
|
||||
|
||||
|
||||
def recover(client, target):
|
||||
_, user = client.request('GET', '/users/' + p.USER)
|
||||
if not p.valid_user(user):
|
||||
raise p.Refused('recovery_user_conflict')
|
||||
tokens = client.pages('/users/' + p.USER + '/tokens')
|
||||
if not tokens:
|
||||
# Absence reconciles a prior ambiguous revocation without new writes.
|
||||
client.journal.put('terminal', outcome='revoke_verified_absent_noop')
|
||||
return
|
||||
if len(tokens) != 1 or tokens[0].get('id') != target['id'] or tokens[0].get('name') != target['name'] or tokens[0].get('scopes') != p.SCOPES:
|
||||
raise p.Refused('recovery_token_inventory_conflict')
|
||||
password = secrets.token_urlsafe(48)
|
||||
client.request('PATCH', '/admin/users/' + p.USER, {'password': password, 'must_change_password': False}, operation='recovery_password_randomize')
|
||||
basic = 'Basic ' + base64.b64encode((p.USER + ':' + password).encode()).decode()
|
||||
try:
|
||||
client.request('DELETE', '/users/' + p.USER + '/tokens/' + str(target['id']), expected=(204, 404), basic=basic, operation='exact_pat_revoke')
|
||||
except p.Refused:
|
||||
# Observation failure does not mean DELETE did not apply. Read-only
|
||||
# reconciliation follows; never issue another DELETE in this execution.
|
||||
pass
|
||||
remaining = client.pages('/users/' + p.USER + '/tokens')
|
||||
if remaining:
|
||||
raise p.Refused('revoke_unverified_stop')
|
||||
client.journal.put('verified', operation='exact_pat_revoke', token_id=target['id'], status='inventory_absent')
|
||||
client.journal.put('terminal', outcome='revoked_verified_kv_preserved')
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--approval', required=True)
|
||||
parser.add_argument('--receipt', required=True)
|
||||
parser.add_argument('--inner', action='store_true', help=argparse.SUPPRESS)
|
||||
args = parser.parse_args(argv)
|
||||
journal, lock = None, None
|
||||
try:
|
||||
target = approved_target(args.approval)
|
||||
if not args.inner:
|
||||
result = subprocess.run(['warden', 'access', 'forgejo-admin-api-token', '--field', 'API_TOKEN', '--exec', '--', sys.executable, str(ROOT / SCRIPT), '--inner', '--approval', str(Path(args.approval).resolve()), '--receipt', str(Path(args.receipt).resolve())], capture_output=True, check=False, timeout=900, cwd=ROOT)
|
||||
return 0 if result.returncode == 0 else 1
|
||||
lock = os.open('/tmp/' + p.CCR + '-reuse-reader-' + str(os.getuid()) + '.lock', os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600)
|
||||
if os.fstat(lock).st_uid != os.getuid() or os.fstat(lock).st_mode & 0o077:
|
||||
raise p.Refused('lane_lock_owner_or_permissions')
|
||||
p.fcntl.flock(lock, p.fcntl.LOCK_EX | p.fcntl.LOCK_NB)
|
||||
journal = p.Journal(args.receipt)
|
||||
admin = os.environ.get('API_TOKEN')
|
||||
if not admin:
|
||||
raise p.Refused('admin_route_missing')
|
||||
recover(p.Client(admin, journal), target)
|
||||
return 0
|
||||
except Exception as error:
|
||||
if journal:
|
||||
journal.put('terminal', outcome='recovery_stopped_reconcile_before_retry', error_code=str(error) if isinstance(error, p.Refused) else 'internal_failure')
|
||||
return 1
|
||||
finally:
|
||||
if journal:
|
||||
journal.close()
|
||||
if lock is not None:
|
||||
os.close(lock)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
result = main()
|
||||
except Exception:
|
||||
result = 1
|
||||
raise SystemExit(result)
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
"""Meaningful fail-closed tests; no credentials or network are used."""
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import hashlib
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
from unittest.mock import patch
|
||||
|
||||
spec = importlib.util.spec_from_file_location('reuse_provision', Path(__file__).parents[1] / 'scripts/provision-reuse-surface-forgejo-source.py')
|
||||
p = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(p)
|
||||
|
||||
class Journal:
|
||||
attempt = 'mock-attempt'
|
||||
def __init__(self): self.rows = []
|
||||
def put(self, event, **kw): self.rows.append((event, kw))
|
||||
|
||||
class Fake:
|
||||
def __init__(self):
|
||||
self.journal = Journal()
|
||||
self.user = None
|
||||
self.team = None
|
||||
self.members = []
|
||||
self.repos = []
|
||||
self.tokens = []
|
||||
self.extra = []
|
||||
self.mutations = []
|
||||
def pages(self, path, sudo=False):
|
||||
if path == '/orgs/coulomb/teams': return [] if self.team is None else [self.team]
|
||||
if path.endswith('/members'): return self.members
|
||||
if path.endswith('/repos') and path.startswith('/teams/'): return self.repos
|
||||
if path == '/user/orgs': return [] if not self.members else [{'username':'coulomb'}]
|
||||
if path == '/user/teams': return [] if not self.members else [{**self.team,'organization':{'username':'coulomb'}}]
|
||||
if path == '/user/repos?order_by=id': return self.repos + self.extra
|
||||
if path.endswith('/tokens'): return self.tokens
|
||||
raise AssertionError(path)
|
||||
def request(self, method, path, payload=None, expected=(200,), **kwargs):
|
||||
if method != 'GET':
|
||||
self.mutations.append((method,path))
|
||||
raise AssertionError('mutation must not occur in preflight failure test')
|
||||
if path == '/users/' + p.USER: return (404,None) if self.user is None else (200,self.user)
|
||||
if path.startswith('/repos/'): return 200, {'private':True,'full_name':'coulomb/'+path.split('/')[3]}
|
||||
raise AssertionError(path)
|
||||
|
||||
class Bao:
|
||||
def __init__(self, data=None): self.data = data
|
||||
def get(self): return self.data
|
||||
|
||||
class Tests(unittest.TestCase):
|
||||
def complete(self):
|
||||
c = Fake()
|
||||
c.user = {'login':p.USER,'restricted':True,'is_admin':False,'visibility':'private'}
|
||||
c.team = {'id':10,'name':p.TEAM,'permission':'read','includes_all_repositories':False,'can_create_org_repo':False,'units_map':p.UNITS}
|
||||
c.members = [{'login':p.USER}]
|
||||
c.repos = [{'name':x,'owner':{'login':p.ORG},'private':True,'full_name':p.ORG+'/'+x} for x in sorted(p.REPOS)]
|
||||
return c
|
||||
def test_server_capped_pages_continue_until_empty(self):
|
||||
c=p.Client('mock-admin',Journal()); pages={1:[{'id':x} for x in range(1,51)],2:[{'id':51}],3:[]}
|
||||
def get(method,path,**kwargs):
|
||||
c.last_total_count=51
|
||||
page=int(path.split('page=')[1]); return 200,pages[page]
|
||||
with patch.object(c,'request',side_effect=get) as request:
|
||||
self.assertEqual(len(c.pages('/user/repos?order_by=id',sudo=True)),51)
|
||||
self.assertEqual(request.call_count,3)
|
||||
def test_late_collaborator_private_grant_zero_mutations(self):
|
||||
c=self.complete()
|
||||
original_pages=c.pages
|
||||
def pages(path,sudo=False):
|
||||
if path.startswith('/user/repos'):
|
||||
return p.Client.pages(c,path,sudo=sudo)
|
||||
return original_pages(path,sudo=sudo)
|
||||
original_request=c.request
|
||||
def request(method,path,**kwargs):
|
||||
if path.startswith('/user/repos?'):
|
||||
page=int(path.split('page=')[1])
|
||||
if page==1: return 200,[{'id':x,'private':False,'full_name':'someone/public-'+str(x),'owner':{'login':'someone'}} for x in range(1,51)]
|
||||
if page==2: return 200,[{'id':51,'private':True,'full_name':'other-org/excluded-collaborator','owner':{'login':'other-org'}}]
|
||||
return 200,[]
|
||||
return original_request(method,path,**kwargs)
|
||||
c.pages=pages; c.request=request
|
||||
with self.assertRaisesRegex(p.Refused,'extra_private_repository_grant'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_duplicate_page_ids_refuse(self):
|
||||
c=p.Client('mock-admin',Journal())
|
||||
with patch.object(c,'request',return_value=(200,[{'id':1}])):
|
||||
with self.assertRaisesRegex(p.Refused,'duplicate_page_id'): c.pages('/user/repos')
|
||||
def test_total_count_mismatch_refuses(self):
|
||||
c=p.Client('mock-admin',Journal())
|
||||
def request(method,path,**kwargs):
|
||||
c.last_total_count=2
|
||||
return 200,[{'id':1}] if 'page=1' in path else []
|
||||
with patch.object(c,'request',side_effect=request):
|
||||
with self.assertRaisesRegex(p.Refused,'total_count_mismatch'): c.pages('/user/repos')
|
||||
def test_unstable_total_count_refuses(self):
|
||||
c=p.Client('mock-admin',Journal())
|
||||
def request(method,path,**kwargs):
|
||||
page=int(path.split('page=')[1]); c.last_total_count=2 if page==1 else 3
|
||||
return 200,[{'id':page}]
|
||||
with patch.object(c,'request',side_effect=request):
|
||||
with self.assertRaisesRegex(p.Refused,'unstable_total_count'): c.pages('/user/repos')
|
||||
def test_excess_pagination_refuses(self):
|
||||
c=p.Client('mock-admin',Journal())
|
||||
def request(method,path,**kwargs):
|
||||
return 200,[{'id':int(path.split('page=')[1])}]
|
||||
with patch.object(c,'request',side_effect=request):
|
||||
with self.assertRaisesRegex(p.Refused,'pagination_bound_exceeded'): c.pages('/user/repos')
|
||||
def test_existing_all_repository_team_refused_before_mutation(self):
|
||||
c=self.complete(); c.team['includes_all_repositories']=True
|
||||
with self.assertRaisesRegex(p.Refused,'team_conflict'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_excluded_team_repo_refused_before_mutation(self):
|
||||
c=self.complete(); c.repos.append({'name':'rapp-core-hub','owner':{'login':'coulomb'}})
|
||||
with self.assertRaisesRegex(p.Refused,'unexpected_team_repository'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_admin_user_refused_before_mutation(self):
|
||||
c=self.complete(); c.user['is_admin']=True
|
||||
with self.assertRaisesRegex(p.Refused,'user_conflict'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_existing_token_kv_binding_mismatch_refused(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'different'}]
|
||||
with self.assertRaisesRegex(p.Refused,'pat_kv_binding_unverifiable'): p.provision(c,Bao({p.FIELD:'mock-token'}),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_extra_private_grants_refused(self):
|
||||
c=self.complete(); c.extra=[{'private':True,'full_name':'coulomb/secret-outside'}]
|
||||
with self.assertRaisesRegex(p.Refused,'extra_private'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_orphan_pat_not_reminted(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'ck-token'}]
|
||||
with self.assertRaisesRegex(p.Refused,'orphan'): p.provision(c,Bao(),{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_verified_noop_rechecks_secret_and_scope(self):
|
||||
c=self.complete(); c.tokens=[{'id':20,'name':p.PREFIX+'one','scopes':p.SCOPES,'token_last_eight':'ck-token'}]
|
||||
with patch.object(p,'verify') as verify:
|
||||
self.assertEqual(p.provision(c,Bao({p.FIELD:'mock-token'}),{}),'noop')
|
||||
verify.assert_called_once_with(c,'mock-token',{})
|
||||
self.assertEqual(c.mutations,[])
|
||||
self.assertEqual(c.journal.rows[-1],('terminal',{'outcome':'verified_noop'}))
|
||||
def test_existing_write_unit_refused(self):
|
||||
c=self.complete(); c.team['units_map']={**p.UNITS,'repo.code':'write'}
|
||||
with self.assertRaisesRegex(p.Refused,'team_conflict'): p.inspect(c,Bao())
|
||||
def test_api_ambiguous_mutation_records_intent_without_raw_error(self):
|
||||
j=Journal(); c=p.Client('fake-secret-admin',j)
|
||||
with patch.object(c.opener,'open',side_effect=RuntimeError('fake-secret-admin sensitive provider body')):
|
||||
with self.assertRaisesRegex(p.Refused,'request_observation_failed'):
|
||||
c.request('POST','/admin/users',{'password':'fake-secret-password'},operation='user_create')
|
||||
self.assertEqual([x[0] for x in j.rows],['intent','outcome'])
|
||||
self.assertNotIn('secret',json.dumps(j.rows))
|
||||
self.assertEqual(j.rows[-1][1]['status'],'ambiguous')
|
||||
def test_network_error_never_reaches_stdout_stderr(self):
|
||||
out,err=io.StringIO(),io.StringIO()
|
||||
with patch.object(p,'approval',return_value={}), patch.object(p,'Journal',return_value=Journal()) as factory, patch.dict(p.os.environ,{'API_TOKEN':'fake-secret-admin'}), patch.object(p,'provision',side_effect=RuntimeError('fake-secret-admin body')):
|
||||
factory.return_value.close=lambda:None
|
||||
with redirect_stdout(out),redirect_stderr(err):
|
||||
code=p.main(['--inner','--approval','mock','--receipt','mock'])
|
||||
self.assertEqual(code,1); self.assertEqual(out.getvalue()+err.getvalue(),'')
|
||||
self.assertNotIn('secret',json.dumps(factory.return_value.rows))
|
||||
def test_unapproved_ccr_refuses_before_warden(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
f=Path(tmp)/'approval.json'; f.write_text(json.dumps({'ccr_id':p.CCR,'decision':'approved','supervisor':'mock','decision_ref':'mock'}))
|
||||
with patch.object(p.subprocess,'run') as run:
|
||||
self.assertEqual(p.main(['--approval',str(f),'--receipt',str(Path(tmp)/'receipt')]),1)
|
||||
run.assert_not_called()
|
||||
def test_approved_artifact_revision_mismatch_before_warden(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp); files={p.CCR_FILE,p.PLAN_FILE,p.SCRIPT_FILE,'openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl'}
|
||||
for rel in files:
|
||||
f=root/rel; f.parent.mkdir(parents=True,exist_ok=True); f.write_text('mock')
|
||||
(root/p.CCR_FILE).write_text(yaml.safe_dump({'status':'approved'}))
|
||||
receipt={'ccr_id':p.CCR,'decision':'approved','supervisor':'founder','decision_ref':'mock-resolved-decision','approved_artifacts':{rel:hashlib.sha256((root/rel).read_bytes()).hexdigest() for rel in files}}
|
||||
f=root/'approval.json'; f.write_text(json.dumps(receipt)); (root/p.SCRIPT_FILE).write_text('modified-after-approval')
|
||||
with patch.object(p,'ROOT',root),patch.object(p.subprocess,'run') as run:
|
||||
self.assertEqual(p.main(['--approval',str(f),'--receipt',str(root/'receipt')]),1)
|
||||
run.assert_not_called()
|
||||
def test_journal_symlink_refused(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
f=Path(tmp)/'real'; f.write_text(''); link=Path(tmp)/'link'; link.symlink_to(f)
|
||||
with self.assertRaises(OSError): p.Journal(link)
|
||||
def test_redirects_never_follow_auth_header(self):
|
||||
with self.assertRaisesRegex(p.Refused,'redirect_refused'):
|
||||
p.NoRedirect().redirect_request(None,None,302,'',{ },'https://other.example')
|
||||
def test_bao_cas_write_uncertain_response_reconciled(self):
|
||||
b=p.Bao(); j=Journal()
|
||||
with patch.object(b,'run',return_value=type('R',(),{'returncode':1})()) as run, patch.object(b,'get',return_value={p.FIELD:'fake-source'}):
|
||||
b.put('fake-source',j)
|
||||
self.assertIn('-cas=0',run.call_args.args[0]); self.assertEqual(run.call_args.args[1],b'fake-source')
|
||||
self.assertEqual(j.rows[-1],('verified',{'operation':'kv_create','status':'matched'}))
|
||||
def test_bao_timeout_preserves_ambiguous_outcome(self):
|
||||
b=p.Bao(); j=Journal()
|
||||
with patch.object(b,'run',side_effect=p.Refused('bao_observation_failed')):
|
||||
with self.assertRaises(p.Refused): b.put('fake-source',j)
|
||||
self.assertEqual(j.rows[-1],('outcome',{'operation':'kv_create','status':'ambiguous'}))
|
||||
def test_kv_denial_not_treated_as_absence(self):
|
||||
b=p.Bao()
|
||||
with patch.object(b,'run',return_value=type('R',(),{'returncode':1,'stderr':b'Code: 403 Error: fake-secret'})()):
|
||||
with self.assertRaisesRegex(p.Refused,'bao_preflight_failed'): b.get()
|
||||
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import importlib.util
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
spec=importlib.util.spec_from_file_location('reuse_recovery',Path(__file__).parents[1]/'scripts/revoke-reuse-surface-forgejo-source.py')
|
||||
r=importlib.util.module_from_spec(spec); spec.loader.exec_module(r)
|
||||
|
||||
class Journal:
|
||||
def __init__(self): self.rows=[]
|
||||
def put(self,event,**kw): self.rows.append((event,kw))
|
||||
class Fake:
|
||||
def __init__(self,ambiguous=False,deleted=True):
|
||||
self.journal=Journal(); self.mutations=[]; self.token={'id':123,'name':r.p.PREFIX+'attempt','scopes':r.p.SCOPES}; self.ambiguous=ambiguous; self.deleted=deleted; self.basic=None
|
||||
def pages(self,path): return [] if self.token is None else [self.token]
|
||||
def request(self,method,path,payload=None,**kw):
|
||||
if method=='GET': return 200,{'login':r.p.USER,'restricted':True,'is_admin':False,'visibility':'private'}
|
||||
self.mutations.append((method,path))
|
||||
if method=='PATCH':
|
||||
assert set(payload)=={'password','must_change_password'}
|
||||
return 200,{}
|
||||
if method=='DELETE':
|
||||
self.basic=kw.get('basic'); assert self.basic.startswith('Basic ')
|
||||
if self.deleted: self.token=None
|
||||
if self.ambiguous: raise r.p.Refused('request_observation_failed')
|
||||
return 204,None
|
||||
raise AssertionError(method)
|
||||
class Tests(unittest.TestCase):
|
||||
def target(self): return {'id':123,'name':r.p.PREFIX+'attempt'}
|
||||
def test_basic_exact_token_revocation(self):
|
||||
c=Fake(); r.recover(c,self.target()); self.assertEqual(c.mutations[-1],('DELETE','/users/'+r.p.USER+'/tokens/123')); self.assertEqual(c.journal.rows[-1][1]['outcome'],'revoked_verified_kv_preserved')
|
||||
def test_unexpected_token_refuses_without_password_change(self):
|
||||
c=Fake(); c.token['id']=124
|
||||
with self.assertRaisesRegex(r.p.Refused,'inventory_conflict'): r.recover(c,self.target())
|
||||
self.assertEqual(c.mutations,[])
|
||||
def test_ambiguous_delete_reconciles_absence_without_retry(self):
|
||||
c=Fake(ambiguous=True); r.recover(c,self.target()); self.assertEqual(sum(m=='DELETE' for m,p in c.mutations),1)
|
||||
def test_ambiguous_delete_still_present_stops(self):
|
||||
c=Fake(ambiguous=True,deleted=False)
|
||||
with self.assertRaisesRegex(r.p.Refused,'revoke_unverified'): r.recover(c,self.target())
|
||||
self.assertEqual(sum(m=='DELETE' for m,p in c.mutations),1)
|
||||
def test_absent_token_verified_noop_has_no_mutations(self):
|
||||
c=Fake(); c.token=None; r.recover(c,self.target()); self.assertEqual(c.mutations,[])
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
id: RPF-WP-0058
|
||||
type: workplan
|
||||
title: Prepare the exact seven private-source reader for reuse-surface
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: proposed
|
||||
owner: codex
|
||||
topic_slug: platform
|
||||
created: '2026-10-15'
|
||||
updated: '2026-10-15'
|
||||
flavor: implementation
|
||||
depends_on: []
|
||||
---
|
||||
|
||||
Custody/Q1 work under platform INTENT.md: dependable shared secret custody and
|
||||
stable delivery with reviewed, delegated apply and revocation. Founder metadata
|
||||
consent is recorded separately and grants no credential or live rollout authority.
|
||||
Preparation occurs in isolated clone; no own supervision record means supervised
|
||||
preparation only. IDs CCR-2026-0046 and RPF-WP-0058 are provisional until integration
|
||||
rechecks the authoritative owner's latest allocation.
|
||||
|
||||
## Prepare bounded custody change
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Prepare CCR-2026-0046, exact seven-source bootstrap contract, silent bounded
|
||||
provisioner, tests and exact ESO policy delta. No provisioner invocation against
|
||||
live service, token read, policy apply, or cluster change has occurred.
|
||||
|
||||
## Independent owner review and founder disposition
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T02
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Platform prepared-review covers own custody artifacts. Forge, apps and consumer
|
||||
owners independently review their scopes; their reviews remain pending. Founder
|
||||
credential provisioning approval must bind exact artifact hashes and confirmed
|
||||
ESO claims. No approval inferred from metadata disclosure. CCR stays proposed.
|
||||
Current consumer image cannot receive this credential: full authenticated
|
||||
redirect/HTTPS plus exact source URL allowlist patch and runtime config required.
|
||||
|
||||
## Execute and verify restricted reader custody
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T03
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
After review and disposition, attended platform lane executes silent bootstrap.
|
||||
Read-only preflight checks all seven private repos, restricted non-admin private
|
||||
user, exact code-read team flags, no unrelated members/teams/private grants and
|
||||
PAT/KV drift. Mutation intent and observed/ambiguous outcome are fsynced. No
|
||||
uncertain mint is retried. CAS=0 never overwrites existing KV. Noop still verifies
|
||||
PAT metadata binding, all seven pinned index digests and excluded-private/admin/
|
||||
Actions denial. Raw provider output and credentials remain suppressed.
|
||||
|
||||
## Owner delivery and hosted completion
|
||||
|
||||
```task
|
||||
id: RPF-WP-0058-T04
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Apps owner integrates proposed data item and exact fourteen approved URL config,
|
||||
performs approved fixed-image rollout after CPU/PVC preflight and safe diff/dryrun.
|
||||
Confirm existing ESO SA reuse/reuse-surface-eso, role, audience and namespace
|
||||
condition from live metadata. Consumer owner native verification precedes private
|
||||
registration; Custodian final fresh hosted audit must prove all70 exactly once.
|
||||
Removing reader grants and delivery must preserve shared writer/webhook fields.
|
||||
|
||||
Revision2 of the same CCR preserves R1 capped-page failure and the independently
|
||||
corrected preliminary owned-only endpoint concern. Twenty bounded pages until
|
||||
empty, duplicate IDs and supplied total-count consistency prevent silent short
|
||||
page acceptance. No live complete-grant proof is claimed by these mocks.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"record_kind": "bounded transcription of successful rmgr sync CLI receipt plus independent remote verification",
|
||||
"observed_at": "2026-10-14T23:27:23.244897+00:00",
|
||||
"repo": "railiance-platform",
|
||||
"source_commit": "a570bc2f5b3afd90c26df76abd40cf6a106cfc94",
|
||||
"identifier_commit": "6e8955fcd09b278a6f16c14beffd076b72d10c95",
|
||||
"sync_command": "uv run --project /home/worsch/repo-manager rmgr sync --path . --push",
|
||||
"sync_exit_code": 0,
|
||||
"sync_status": "applied",
|
||||
"instance_role": "primary",
|
||||
"instance_label": "railiance01",
|
||||
"expected_commit": "6e8955fcd09b278a6f16c14beffd076b72d10c95",
|
||||
"derived_commit": "6e8955fcd09b278a6f16c14beffd076b72d10c95",
|
||||
"upstream": "origin/main",
|
||||
"ahead": 0,
|
||||
"behind": 0,
|
||||
"remote_main_verified": "6e8955fcd09b278a6f16c14beffd076b72d10c95",
|
||||
"managed_identifier_changes": "Only five UUID fields added to RPF-WP-0058 source; executable/CCR/contract/policy bytes unchanged.",
|
||||
"workplan_uuid": "b55a8aec-7457-563b-990b-4a55ca04d2f6",
|
||||
"task_uuids": [
|
||||
"f9b738e1-6d88-5dde-9fc5-b05b3adf2549",
|
||||
"41d16c6e-9e0b-543b-b600-9288d5762879",
|
||||
"58caf089-9af1-51fd-a94d-aba9abf14a53",
|
||||
"9c483fd9-68a5-56b0-813d-c965a11b60d2"
|
||||
],
|
||||
"unrelated_file_preserved": "WORK-RECORDS.md remained unstaged; no other uncommitted file touched.",
|
||||
"pipeline_inspection": "CI smoke and path-filtered KeyCape unittest only; neither applies OpenBao/K8s. Policy path outside ArgoCD resource source.",
|
||||
"credential_status": "CCR proposed, claims unconfirmed; no credential provisioning, token reads or live policy/deployment mutation.",
|
||||
"progress_id": "dc72be1e-027c-4ccf-822c-17bdea83c359"
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"schema": "cust81.owner-identity-disposition.v1",
|
||||
"proposal_id": "CUST81-MASON-CLOSED-UNBOUND-IDENTITY-R1",
|
||||
"revision": 1,
|
||||
"checked_at": "2026-10-14T23:46:59.325635+00:00",
|
||||
"owner": "repo-manager",
|
||||
"disposition": "deferred_paired_receiving_and_registrar_repair_required",
|
||||
"implementation_authorized": false,
|
||||
"mutation_performed": false,
|
||||
"record": {
|
||||
"repo": "ops-mason",
|
||||
"source_path": "workplans/ADHOC-2026-10-11.md",
|
||||
"source_sha256": "7fadf4475ff765999b71659495b786737a9641c8ad875afc97bd264a8c9f0aec",
|
||||
"parent_id": "ADHOC-2026-10-11",
|
||||
"parent_uuid": "e3854bfd-2bc8-5411-8c96-44ed22d878a7",
|
||||
"task_id": "ADHOC-2026-10-11-T03",
|
||||
"status": "done",
|
||||
"existing_task_uuid": null
|
||||
},
|
||||
"source_contracts": [
|
||||
{
|
||||
"path": "/home/worsch/repo-manager/docs/work-record-uuid-derivation_v1.md",
|
||||
"sha256": "600f5c16a0f9325df56dd698311d0151571742782664a2d2bc791b8f5d964157"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/repo-manager/src/repo_manager/identifiers.py",
|
||||
"sha256": "68dbbd3f137f326a49d20ba3f5855d6b00b4d62eb01dc904b51a08d77552d2bf"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/state-hub/api/services/forge_projection.py",
|
||||
"sha256": "b980e62cbd85c3e7df42b01abf14354e04133374db94af6531dcd91629c3487d"
|
||||
}
|
||||
],
|
||||
"read_only_receiving_counterexample": {
|
||||
"method": "derive_from_checkout against actual ops-mason source, offline, no reconciliation",
|
||||
"historical_preservation": false,
|
||||
"would_derive_task_uuid": "5b08aebc-4b99-55ee-8d3d-07c42d51046c",
|
||||
"interpretation": "Current receiver includes T03 as a new derived task. A registrar-only skip would allow prohibited historical allocation."
|
||||
},
|
||||
"required_future_repair": [
|
||||
"Exact immutable source-bound receiver disposition excludes never-bound closed T03 while preserving existing parent, T01/T02 and every source byte.",
|
||||
"Independent tests prove no historical task creation, no existing UUID/status/parent mutation, strict refusal on source/status/identity drift and unknown/live missing legacy tasks.",
|
||||
"Receiving owner deployment approval/proof before activating matching registrar admission; normal rmgr sync only after both contracts are established."
|
||||
],
|
||||
"record_routing": {
|
||||
"current_evidence_owner": "MASON-WP-0013 (existing source publication wait) and CUST-WP-0081-T02 (reader preparation evidence only)",
|
||||
"completed_precedents": [
|
||||
"RMGR-WP-0019",
|
||||
"STATE-WP-0095"
|
||||
],
|
||||
"other_related_open_intake": "CUST-IN-0017 is Custodian-specific residual; do not expand it into fleet repair without triage/promotion",
|
||||
"new_repair_workplan_created": false
|
||||
},
|
||||
"reader_execution_dependency": false,
|
||||
"notes": [
|
||||
"Mason source b67b57a remains local and untouched. Published Platform CCR/executables and consumer/apps/Forge reviews govern the current reader proposal.",
|
||||
"No UUID allocated, historic record rewritten, direct push, normal sync retry, production change or credential action."
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-CONSUMER-CANDIDATE-REVIEW-R1",
|
||||
"reviewed_at": "2026-10-14T23:08:33.016513+00:00",
|
||||
"review_scope": "Consumer code, receipt-only verification helper, exact metadata URL configuration and local image preparation integration only; not credential provisioning, production rollout, registry admission or runtime approval.",
|
||||
"owner_review": {
|
||||
"reviewer": "reuse-surface-owner (agent reuse-surface, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "REUSE-WP-0025-T02. INTENT.md Registry First, Consumer Experience Matters, and Internal and External Evidence Must Stay Separate: the reviewed native patch makes source credentials usable only for operator-approved exact HTTPS URLs, before any caller-selected credential lookup; redirects are refused. The helper verifies native acquisition of the consented seven indexes and seven entries by exact hashes/schema without stale-cache fallback and returns bounded receipts. Exact candidate source/helper/config/test and preparation patch digests are bound below. 227 tests pass in explicit fixture environment and source hashes match the locally smoke-verified image. This approves consumer preparation integration only; baseline proposal unchanged-image reader delivery remains changes_requested. No credential execution, founder approval or production success is inferred."
|
||||
},
|
||||
"workplan_id": "REUSE-WP-0025",
|
||||
"task_id": "REUSE-WP-0025-T02",
|
||||
"intent_sha256": "511d1029a7aefc67b68a6363ed3ce8f803f2516c00062ff805263dffe26c771b",
|
||||
"candidate_artifacts": {
|
||||
"reuse_surface/federation.py": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
"reuse_surface/private_source_verify.py": "c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c",
|
||||
"tests/test_private_source_verify.py": "5f0c0b30b98b4db96d695bdda952b25707916e297d566744fadb6dde7bea18b2",
|
||||
"docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
"docs/assessments/cust81-private-reader/suite-environment-validation.json": "84f9c410f09e0dd5c0c90987744bc9f6e85292d2e28e4db88859ee0b24e68bd2",
|
||||
"docs/assessments/cust81-private-reader/reuse-owner-preparation-r1.patch": "da9634481ce80658dfce815d3539ba023234938dc76163dfd93d7677be95ed51"
|
||||
},
|
||||
"local_image": {
|
||||
"manifest_digest": "sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
|
||||
"receipt_path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/hardened-reader-local-image-r1.json",
|
||||
"receipt_sha256": "ed7c2516925d7f16b573a70b5e48d5656a4875e9eb94bcc8760a8bdcbe07f59c",
|
||||
"published": false,
|
||||
"deployed": false,
|
||||
"credentials_provisioned": false
|
||||
},
|
||||
"existing_auth_source_inventory": {
|
||||
"receipt_path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/live-authenticated-source-inventory-r1.json",
|
||||
"receipt_sha256": "ba669d6231c8c611191c7ad28f5914d6c47332032b23b0ea40a13f753e883073",
|
||||
"checked_at": "2026-10-14T23:01:13.254261+00:00",
|
||||
"authenticated_source_count": 0,
|
||||
"must_recheck_before_rollout": true
|
||||
},
|
||||
"preserved_baseline_review": {
|
||||
"path": "docs/assessments/cust81-private-reader/owner-review-r1.md",
|
||||
"decision": "changes_requested",
|
||||
"scope": "Original R1 same-image credential delivery proposal; preserved, not rewritten."
|
||||
},
|
||||
"remaining_execution_gates": [
|
||||
"Platform/Forgejo/apps/mason owner reviews of exact current CCR and implementation.",
|
||||
"Exact founder credential and live rollout decision remains separate.",
|
||||
"Publish and independently verify registry image digest before rendering actual release values.",
|
||||
"Recheck metadata-only authenticated source inventory and materialized exact14 reader allowlist before approved rollout.",
|
||||
"Run receipt helper in actual hub environment with native authenticated fresh acquisition, then exact70 hosted audit."
|
||||
],
|
||||
"does_not_approve": [
|
||||
"Credential provision or access grant",
|
||||
"Live production mutation or rollout",
|
||||
"Other owner roles",
|
||||
"Registration/admission status change",
|
||||
"Entire objective completion"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-CCR0046-CONSUMER-REVIEW-R2",
|
||||
"reviewed_at": "2026-10-14T23:19:37.233425+00:00",
|
||||
"workplan_id": "REUSE-WP-0025",
|
||||
"task_id": "REUSE-WP-0025-T01",
|
||||
"ccr_id": "CCR-2026-0046",
|
||||
"revision": 2,
|
||||
"review": {
|
||||
"reviewer": "reuse-surface-owner (agent reuse-surface, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "REUSE-WP-0025-T01; INTENT.md Registry First, Consumer Experience Matters and Internal and External Evidence Must Stay Separate. Reviewed exact CCR-2026-0046 revision2, provisioner, exact-token recovery, bootstrap contract and ESO policy SHA256 listed below solely for consumer delivery boundary. The exact seven consented sources/commits/hash rows match original pinned disclosure preparation. Separate reader KV and env protect scoped writer callers; contract prohibits delivery to baseline/current image and requires full reviewed HTTPS/redirect/credential-before-use allowlist enforcement plus exact fourteen metadata URLs. Final actual hub helper must prove native index AND entry hashes without stale cache; 70-ID hosted audit remains independent. Recovery confines revocation to separately approved exact dedicated PAT, preserves shared runtime KV/Secret/public sources, and does not remint after ambiguity. Full native code/helper/config preparation integration review is separately approved and pinned below. This owner judgement approves preparation integration of this consumer boundary, not human credential/rollout go, provisioner execution, status/admission changes, other owners or deployment success."
|
||||
},
|
||||
"artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "90522912fb78fed72e625382fb7114a0e9aafe76d59e36bffecab83c5bb7d643",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "abf60a754596baa1610d23b1fd367385b0222ac8aeaebba0f2cfb7ec4c62055b",
|
||||
"scripts/revoke-reuse-surface-forgejo-source.py": "f78d1b26fbff93c6cc481a896d9fb9b9ce93049b8b3267fa41a64bc57dadb162",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "2bb72671e0a5f230497ab8ef2967a20ee68d1f2cf081f07c84f6db3442e18d8b",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b"
|
||||
},
|
||||
"consumer_preparation_review": {
|
||||
"path": "docs/assessments/cust81-private-reader/candidate-owner-review-r1.json",
|
||||
"sha256": "853cb3b721d83ba5aeb0847111cc47f8094b733f870059cca9834b2c1a5a6219",
|
||||
"decision": "approved"
|
||||
},
|
||||
"preserved_baseline": "Original same-image reader delivery review remains changes_requested; this R2 consumer boundary resolves that design defect without altering earlier evidence.",
|
||||
"execution_authorized": false,
|
||||
"required_before_execution": [
|
||||
"Human exact credential and separately scoped rollout decisions, with all required independent owner reviews.",
|
||||
"Published independently verified image digest and reviewed release config for the full security patch.",
|
||||
"Recheck metadata-only live authenticated-source inventory (last snapshot0) and validate exact14 runtime reader URLs.",
|
||||
"Post-rollout native authenticated helper receipt plus separate registration and exact70 hosted completion audit."
|
||||
],
|
||||
"not_proven": [
|
||||
"Real Forgejo reader scope",
|
||||
"Live OpenBao/ESO materialization",
|
||||
"Production image/config rollout",
|
||||
"Actual authenticated metadata fetch",
|
||||
"Hosted private capability admission"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from datetime import date, datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
from jsonschema import Draft202012Validator
|
||||
|
||||
from reuse_surface.registry import ROOT
|
||||
|
||||
MANIFEST_PATH = ROOT / "registry" / "federation" / "sources.yaml"
|
||||
SCHEMA_PATH = ROOT / "schemas" / "federation.schema.yaml"
|
||||
FEDERATED_INDEX_PATH = ROOT / "registry" / "indexes" / "federated.yaml"
|
||||
CACHE_DIR = ROOT / "registry" / "federation" / "cache"
|
||||
DEFAULT_CACHE_TTL_SECONDS = 86400
|
||||
USER_AGENT = "reuse-surface/0.1 federation-compose"
|
||||
|
||||
|
||||
def _expand_path(index_path: str) -> Path:
|
||||
return Path(index_path).expanduser()
|
||||
|
||||
|
||||
def load_federation_manifest(path: Path | None = None) -> dict[str, Any]:
|
||||
manifest_path = path or MANIFEST_PATH
|
||||
with manifest_path.open(encoding="utf-8") as handle:
|
||||
manifest = yaml.safe_load(handle)
|
||||
schema = yaml.safe_load(SCHEMA_PATH.read_text(encoding="utf-8"))
|
||||
validator = Draft202012Validator(schema)
|
||||
errors = sorted(validator.iter_errors(manifest), key=lambda err: err.path)
|
||||
if errors:
|
||||
messages = "; ".join(error.message for error in errors)
|
||||
raise ValueError(f"invalid federation manifest: {messages}")
|
||||
return manifest
|
||||
|
||||
|
||||
def _resolve_index_path(index_value: str) -> Path:
|
||||
path = _expand_path(index_value)
|
||||
if not path.is_absolute():
|
||||
path = (ROOT / path).resolve()
|
||||
return path
|
||||
|
||||
|
||||
def _safe_repo_slug(repo: str) -> str:
|
||||
return re.sub(r"[^\w.-]", "_", repo)
|
||||
|
||||
|
||||
def _path_label(path: Path) -> str:
|
||||
try:
|
||||
return str(path.relative_to(ROOT))
|
||||
except ValueError:
|
||||
return str(path)
|
||||
|
||||
|
||||
def _cache_root(cache_dir: Path | None = None) -> Path:
|
||||
return cache_dir or CACHE_DIR
|
||||
|
||||
|
||||
def _cache_paths(repo: str, cache_dir: Path | None = None) -> tuple[Path, Path]:
|
||||
root = _cache_root(cache_dir)
|
||||
slug = _safe_repo_slug(repo)
|
||||
return root / f"{slug}.yaml", root / f"{slug}.meta.yaml"
|
||||
|
||||
|
||||
def _read_cache_meta(meta_path: Path) -> dict[str, Any] | None:
|
||||
if not meta_path.exists():
|
||||
return None
|
||||
data = yaml.safe_load(meta_path.read_text(encoding="utf-8"))
|
||||
return data if isinstance(data, dict) else None
|
||||
|
||||
|
||||
def _cache_is_fresh(meta: dict[str, Any], ttl_seconds: int) -> bool:
|
||||
if ttl_seconds <= 0:
|
||||
return False
|
||||
fetched_at = meta.get("fetched_at")
|
||||
if not fetched_at:
|
||||
return False
|
||||
fetched = datetime.fromisoformat(str(fetched_at))
|
||||
if fetched.tzinfo is None:
|
||||
fetched = fetched.replace(tzinfo=timezone.utc)
|
||||
age = (datetime.now(timezone.utc) - fetched).total_seconds()
|
||||
return age < ttl_seconds
|
||||
|
||||
|
||||
def _auth_headers(source: dict[str, Any]) -> dict[str, str]:
|
||||
auth_env = source.get("auth_env")
|
||||
if not auth_env:
|
||||
return {}
|
||||
token = os.environ.get(auth_env)
|
||||
if not token:
|
||||
raise ValueError(
|
||||
f"auth env {auth_env} is not set for remote source {source['repo']}"
|
||||
)
|
||||
header_name = source.get("auth_header", "Authorization")
|
||||
if header_name.lower() == "authorization" and not token.lower().startswith(
|
||||
("bearer ", "basic ")
|
||||
):
|
||||
token = f"Bearer {token}"
|
||||
return {header_name: token}
|
||||
|
||||
|
||||
class _RejectAuthenticatedRedirect(urllib.request.HTTPRedirectHandler):
|
||||
"""Never replay a source credential at a redirect target.
|
||||
|
||||
A private source must register its final URL. This also rejects same-origin
|
||||
redirects because a path change can escape the reviewed repository scope.
|
||||
"""
|
||||
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
raise ConnectionError("authenticated source redirect refused")
|
||||
|
||||
|
||||
def fetch_remote_index_text(url: str, source: dict[str, Any]) -> str:
|
||||
if source.get("auth_env"):
|
||||
if urllib.parse.urlsplit(url).scheme.lower() != "https":
|
||||
raise ValueError("authenticated source requires HTTPS")
|
||||
# Registrations are writable data, not credential-routing authority.
|
||||
# Operators must bind each allowed environment name to exact URLs in
|
||||
# runtime configuration before the credential itself can be read.
|
||||
try:
|
||||
allowlist = json.loads(os.environ.get("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", "{}"))
|
||||
allowed_urls = allowlist.get(source["auth_env"], [])
|
||||
permitted = isinstance(allowed_urls, list) and url in allowed_urls
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
permitted = False
|
||||
if not permitted:
|
||||
raise ValueError("authenticated source URL is not runtime-authorized")
|
||||
auth_headers = _auth_headers(source)
|
||||
headers = {"User-Agent": USER_AGENT, **auth_headers}
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
# urllib's default redirect handler copies Authorization to the target.
|
||||
# Anonymous federation retains its existing redirect behavior.
|
||||
opener = (
|
||||
urllib.request.build_opener(_RejectAuthenticatedRedirect()).open
|
||||
if auth_headers
|
||||
else urllib.request.urlopen
|
||||
)
|
||||
try:
|
||||
with opener(request, timeout=30) as response:
|
||||
return response.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as exc:
|
||||
raise ConnectionError(
|
||||
f"HTTP {exc.code} fetching {url} for {source['repo']}"
|
||||
) from exc
|
||||
except urllib.error.URLError as exc:
|
||||
raise ConnectionError(
|
||||
f"failed to fetch {url} for {source['repo']}: {exc.reason}"
|
||||
) from exc
|
||||
|
||||
|
||||
def _write_remote_cache(
|
||||
repo: str, url: str, content: str, cache_dir: Path | None = None
|
||||
) -> Path:
|
||||
root = _cache_root(cache_dir)
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
index_path, meta_path = _cache_paths(repo, cache_dir)
|
||||
index_path.write_text(content, encoding="utf-8")
|
||||
meta = {
|
||||
"fetched_at": datetime.now(timezone.utc).isoformat(),
|
||||
"url": url,
|
||||
"repo": repo,
|
||||
}
|
||||
meta_path.write_text(yaml.safe_dump(meta, sort_keys=False), encoding="utf-8")
|
||||
return index_path
|
||||
|
||||
|
||||
def resolve_source_index_path(
|
||||
source: dict[str, Any],
|
||||
*,
|
||||
refresh: bool = False,
|
||||
cache_dir: Path | None = None,
|
||||
) -> tuple[Path | None, list[str]]:
|
||||
warnings: list[str] = []
|
||||
if "index" in source:
|
||||
path = _resolve_index_path(source["index"])
|
||||
if not path.exists():
|
||||
message = f"missing index for {source['repo']}: {path}"
|
||||
if source.get("required", False):
|
||||
raise FileNotFoundError(message)
|
||||
warnings.append(message)
|
||||
return None, warnings
|
||||
return path, warnings
|
||||
|
||||
url = source["url"]
|
||||
ttl_seconds = int(source.get("cache_ttl_seconds", DEFAULT_CACHE_TTL_SECONDS))
|
||||
index_path, meta_path = _cache_paths(source["repo"], cache_dir)
|
||||
meta = _read_cache_meta(meta_path)
|
||||
use_cache = (
|
||||
index_path.exists()
|
||||
and meta is not None
|
||||
and meta.get("url") == url
|
||||
and _cache_is_fresh(meta, ttl_seconds)
|
||||
and not refresh
|
||||
)
|
||||
if use_cache:
|
||||
return index_path, warnings
|
||||
|
||||
try:
|
||||
content = fetch_remote_index_text(url, source)
|
||||
except (ConnectionError, ValueError) as exc:
|
||||
if index_path.exists():
|
||||
warnings.append(
|
||||
f"remote fetch failed for {source['repo']}, using stale cache: {exc}"
|
||||
)
|
||||
return index_path, warnings
|
||||
message = f"remote index unavailable for {source['repo']}: {exc}"
|
||||
if source.get("required", False):
|
||||
raise FileNotFoundError(message) from exc
|
||||
warnings.append(message)
|
||||
return None, warnings
|
||||
|
||||
return _write_remote_cache(source["repo"], url, content, cache_dir), warnings
|
||||
|
||||
|
||||
SCOPE_BLOCK_KEYS = {"type", "title", "description", "keywords"}
|
||||
|
||||
|
||||
def _shape_hint(item: dict[str, Any]) -> str:
|
||||
"""Name the likely mistake when an index row has no id.
|
||||
|
||||
The fenced `capability` blocks in a repo's SCOPE.md use
|
||||
type/title/description/keywords, which is close enough to an index row to
|
||||
be copied into one by mistake — and then the member silently contributes
|
||||
nothing. Say so instead of leaving the author to guess.
|
||||
"""
|
||||
if SCOPE_BLOCK_KEYS & set(item):
|
||||
return (
|
||||
" — looks like a SCOPE.md capability block"
|
||||
" (type/title/description/keywords); an index row needs"
|
||||
" id/name/summary/vector/domain/status/owner/path"
|
||||
)
|
||||
return ""
|
||||
|
||||
|
||||
def _read_index_entries(
|
||||
index_path: Path, repo: str
|
||||
) -> tuple[list[Any], list[str]]:
|
||||
"""Read the capability rows of one member index.
|
||||
|
||||
A malformed member index must not abort the whole compose, so every
|
||||
failure here degrades to a warning and an empty row list.
|
||||
"""
|
||||
try:
|
||||
with index_path.open(encoding="utf-8") as handle:
|
||||
index_data = yaml.safe_load(handle)
|
||||
except yaml.YAMLError as exc:
|
||||
return [], [f"{repo}: unparseable index, skipped ({exc.__class__.__name__})"]
|
||||
if not isinstance(index_data, dict):
|
||||
return [], [f"{repo}: index is not a mapping, skipped"]
|
||||
entries = index_data.get("capabilities") or []
|
||||
if not isinstance(entries, list):
|
||||
return [], [f"{repo}: capabilities is not a list, skipped"]
|
||||
return entries, []
|
||||
|
||||
|
||||
def compose_federated_index(
|
||||
manifest: dict[str, Any] | None = None,
|
||||
*,
|
||||
refresh: bool = False,
|
||||
cache_dir: Path | None = None,
|
||||
) -> tuple[dict[str, Any], list[str]]:
|
||||
manifest = manifest or load_federation_manifest()
|
||||
warnings: list[str] = []
|
||||
merged: list[dict[str, Any]] = []
|
||||
seen_ids: dict[str, str] = {}
|
||||
source_summaries: list[dict[str, Any]] = []
|
||||
|
||||
for source in manifest["sources"]:
|
||||
if not source.get("enabled", False):
|
||||
continue
|
||||
index_path, source_warnings = resolve_source_index_path(
|
||||
source, refresh=refresh, cache_dir=cache_dir
|
||||
)
|
||||
warnings.extend(source_warnings)
|
||||
if index_path is None:
|
||||
continue
|
||||
entries, read_warnings = _read_index_entries(index_path, source["repo"])
|
||||
warnings.extend(read_warnings)
|
||||
count = 0
|
||||
for position, item in enumerate(entries):
|
||||
if not isinstance(item, dict):
|
||||
warnings.append(f"{source['repo']}: capability #{position} is not a mapping, skipped")
|
||||
continue
|
||||
cap_id = item.get("id")
|
||||
if not cap_id:
|
||||
warnings.append(
|
||||
f"{source['repo']}: capability #{position} has no id, skipped"
|
||||
f"{_shape_hint(item)}"
|
||||
)
|
||||
continue
|
||||
if cap_id in seen_ids:
|
||||
warnings.append(
|
||||
f"duplicate id {cap_id}: {seen_ids[cap_id]} and {source['repo']}"
|
||||
)
|
||||
else:
|
||||
seen_ids[cap_id] = source["repo"]
|
||||
federated_item = dict(item)
|
||||
federated_item["source_repo"] = source["repo"]
|
||||
if "url" in source:
|
||||
federated_item["source_url"] = source["url"]
|
||||
federated_item["source_index"] = _path_label(index_path)
|
||||
else:
|
||||
federated_item["source_index"] = source["index"]
|
||||
merged.append(federated_item)
|
||||
count += 1
|
||||
summary: dict[str, Any] = {
|
||||
"repo": source["repo"],
|
||||
"count": count,
|
||||
}
|
||||
if "url" in source:
|
||||
summary["url"] = source["url"]
|
||||
summary["cache"] = _path_label(index_path)
|
||||
else:
|
||||
summary["index"] = source["index"]
|
||||
source_summaries.append(summary)
|
||||
|
||||
federated = {
|
||||
"version": manifest.get("version", 1),
|
||||
"updated": date.today().isoformat(),
|
||||
"domain": manifest.get("domain"),
|
||||
"collision_policy": manifest.get("collision_policy", "warn"),
|
||||
"sources": source_summaries,
|
||||
"capabilities": sorted(merged, key=lambda item: item["id"]),
|
||||
}
|
||||
return federated, warnings
|
||||
|
||||
|
||||
def write_federated_index(
|
||||
output_path: Path | None = None,
|
||||
manifest: dict[str, Any] | None = None,
|
||||
*,
|
||||
refresh: bool = False,
|
||||
) -> tuple[Path, list[str]]:
|
||||
federated, warnings = compose_federated_index(manifest, refresh=refresh)
|
||||
target = output_path or FEDERATED_INDEX_PATH
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
header = (
|
||||
"# Composed federated capability index. Regenerate with:\n"
|
||||
"# reuse-surface federation compose\n"
|
||||
)
|
||||
target.write_text(
|
||||
header + yaml.safe_dump(federated, sort_keys=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return target, warnings
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
"""Owner-prepared, receipt-only native acquisition check for CUST81 revision 1.
|
||||
|
||||
Run inside the hub environment after a separately approved credential rollout:
|
||||
python -m reuse_surface.private_source_verify --proposal FILE --disclosure FILE
|
||||
--consent FILE
|
||||
No credentials are accepted as arguments. No bodies or exception text are emitted.
|
||||
This command does not register sources or use the federation cache.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
import yaml
|
||||
from jsonschema import Draft202012Validator
|
||||
|
||||
from reuse_surface import federation
|
||||
from reuse_surface.registry import load_schema
|
||||
|
||||
PROPOSAL_SHA256 = "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99"
|
||||
DISCLOSURE_SHA256 = "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35"
|
||||
CONSENT_SHA256 = "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52"
|
||||
AUTH_ENV = "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
REPOSITORIES = frozenset({
|
||||
"rapp-openbao", "rapp-policy-nexus", "rapp-postgres", "rapp-sbom-nexus",
|
||||
"rapp-telemetry", "rapp-tenant-engine", "rapp-user-engine",
|
||||
})
|
||||
|
||||
|
||||
def _sha(content: bytes) -> str:
|
||||
return hashlib.sha256(content).hexdigest()
|
||||
|
||||
|
||||
def _load(path: Path, expected: str) -> dict[str, Any]:
|
||||
data = path.read_bytes()
|
||||
if _sha(data) != expected:
|
||||
raise ValueError("packet_digest_mismatch")
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
def _raw_url(repo: str, commit: str, path: str) -> str:
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", commit):
|
||||
raise ValueError("invalid_commit")
|
||||
if not re.fullmatch(r"registry/(indexes|capabilities)/[a-zA-Z0-9._-]+\.(yaml|md)", path):
|
||||
raise ValueError("invalid_metadata_path")
|
||||
return (
|
||||
f"https://forgejo.coulomb.social/api/v1/repos/coulomb/{repo}/raw/"
|
||||
f"{quote(path, safe='/')}?ref={commit}"
|
||||
)
|
||||
|
||||
|
||||
def _prepared_sources(proposal_path: Path, disclosure_path: Path, consent_path: Path):
|
||||
proposal = _load(proposal_path, PROPOSAL_SHA256)
|
||||
disclosure = _load(disclosure_path, DISCLOSURE_SHA256)
|
||||
consent = _load(consent_path, CONSENT_SHA256)
|
||||
if (
|
||||
consent["consent_status"] != "approved"
|
||||
or consent["proposal_sha256"] != DISCLOSURE_SHA256
|
||||
or consent["proposal_id"] != disclosure["id"]
|
||||
or consent["proposal_revision"] != disclosure["revision"]
|
||||
or set(consent["repositories"]) != REPOSITORIES
|
||||
):
|
||||
raise ValueError("invalid_consent")
|
||||
sources = proposal["sources"]
|
||||
approved = {source["repo"]: source for source in disclosure["sources"]}
|
||||
if len(sources) != 7 or {source["repo"] for source in sources} != REPOSITORIES:
|
||||
raise ValueError("invalid_source_scope")
|
||||
for source in sources:
|
||||
owner = approved[source["repo"]]
|
||||
files = {item["path"]: item["sha256"] for item in owner["files"]}
|
||||
if (
|
||||
source["consented_commit"] != owner["commit"]
|
||||
or source["capability_ids"] != owner["capability_ids"]
|
||||
or source["index_sha256"] != files["registry/indexes/capabilities.yaml"]
|
||||
or source["auth_env"] != AUTH_ENV
|
||||
or source["native_raw_url"] != _raw_url(
|
||||
source["repo"], owner["commit"], "registry/indexes/capabilities.yaml"
|
||||
)
|
||||
):
|
||||
raise ValueError("source_not_consented")
|
||||
yield source, files
|
||||
|
||||
|
||||
def verify(proposal_path: Path, disclosure_path: Path, consent_path: Path) -> dict[str, Any]:
|
||||
receipt: dict[str, Any] = {
|
||||
"id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1",
|
||||
"checked_at": datetime.now(timezone.utc).isoformat(),
|
||||
"proposal_sha256": PROPOSAL_SHA256,
|
||||
"disclosure_sha256": DISCLOSURE_SHA256,
|
||||
"consent_sha256": CONSENT_SHA256,
|
||||
"cache_used": False,
|
||||
"all_verified": False,
|
||||
"sources": [],
|
||||
}
|
||||
try:
|
||||
# Exhaust all packet checks before any native network request.
|
||||
sources = list(_prepared_sources(proposal_path, disclosure_path, consent_path))
|
||||
expected_urls = {
|
||||
_raw_url(source["repo"], source["consented_commit"], path)
|
||||
for source, files in sources for path in files
|
||||
}
|
||||
allowlist = json.loads(os.environ.get("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", "{}"))
|
||||
actual_urls = allowlist.get(AUTH_ENV)
|
||||
if (
|
||||
not isinstance(actual_urls, list)
|
||||
or len(actual_urls) != 14
|
||||
or set(actual_urls) != expected_urls
|
||||
):
|
||||
raise ValueError("runtime_scope_not_exact")
|
||||
schema = Draft202012Validator(load_schema())
|
||||
except Exception:
|
||||
receipt["error"] = "packet_scope_or_schema_validation_failed"
|
||||
return receipt
|
||||
for source, files in sources:
|
||||
row = {
|
||||
"repo": source["repo"],
|
||||
"commit": source["consented_commit"],
|
||||
"index_sha256": source["index_sha256"],
|
||||
"verified": False,
|
||||
"entry_sha256": [],
|
||||
}
|
||||
receipt["sources"].append(row)
|
||||
try:
|
||||
index_text = federation.fetch_remote_index_text(source["native_raw_url"], source)
|
||||
if _sha(index_text.encode("utf-8")) != source["index_sha256"]:
|
||||
raise ValueError("index_hash_mismatch")
|
||||
index = yaml.safe_load(index_text)
|
||||
entries = index["capabilities"]
|
||||
if not isinstance(entries, list) or [e["id"] for e in entries] != source["capability_ids"]:
|
||||
raise ValueError("unexpected_capability_ids")
|
||||
for entry in entries:
|
||||
if not {"id", "name", "summary", "vector", "domain", "status", "owner", "path"} <= entry.keys():
|
||||
raise ValueError("invalid_index_row")
|
||||
if entry["owner"] != source["repo"] or entry["path"] not in files:
|
||||
raise ValueError("unconsented_entry")
|
||||
entry_url = _raw_url(source["repo"], source["consented_commit"], entry["path"])
|
||||
entry_text = federation.fetch_remote_index_text(entry_url, source)
|
||||
digest = _sha(entry_text.encode("utf-8"))
|
||||
if digest != files[entry["path"]]:
|
||||
raise ValueError("entry_hash_mismatch")
|
||||
front = re.match(r"^---\n(.*?)\n---", entry_text, re.DOTALL)
|
||||
if front is None:
|
||||
raise ValueError("missing_front_matter")
|
||||
metadata = yaml.safe_load(front.group(1))
|
||||
schema.validate(metadata)
|
||||
if metadata["id"] != entry["id"] or metadata["owner"] != source["repo"]:
|
||||
raise ValueError("entry_identity_mismatch")
|
||||
row["entry_sha256"].append(digest)
|
||||
row["verified"] = True
|
||||
except Exception:
|
||||
# Never stringify errors: HTTP handlers and parsers can embed bodies,
|
||||
# headers, credentials, or private source text in their messages.
|
||||
row["error"] = "native_fetch_or_metadata_validation_failed"
|
||||
receipt["all_verified"] = len(receipt["sources"]) == 7 and all(
|
||||
row["verified"] for row in receipt["sources"]
|
||||
)
|
||||
return receipt
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
for name in ("proposal", "disclosure", "consent"):
|
||||
parser.add_argument(f"--{name}", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
receipt = verify(args.proposal, args.disclosure, args.consent)
|
||||
print(json.dumps(receipt, sort_keys=True))
|
||||
return 0 if receipt["all_verified"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
import hashlib
|
||||
import io
|
||||
import json
|
||||
from email.message import Message
|
||||
from urllib.request import HTTPSHandler, build_opener
|
||||
from urllib.response import addinfourl
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from reuse_surface import federation, private_source_verify as verifier
|
||||
|
||||
|
||||
def _digest(text):
|
||||
return hashlib.sha256(text.encode()).hexdigest()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def packets(tmp_path, monkeypatch):
|
||||
"""Fake owner-consented bodies; no estate credential or network transport."""
|
||||
approved, sources, bodies = [], [], {}
|
||||
for repo in sorted(verifier.REPOSITORIES):
|
||||
cap_id = f"capability.fixture.{repo}"
|
||||
commit = "a" * 40
|
||||
path = f"registry/capabilities/{cap_id}.md"
|
||||
metadata = {
|
||||
"id": cap_id, "name": "Fixture", "summary": "Fixture metadata",
|
||||
"owner": repo, "status": "draft",
|
||||
"maturity": {"discovery": {"current": "D3", "target": "D5"}, "availability": {"current": "A2", "target": "A5"}},
|
||||
"external_evidence": {"completeness": {"level": "C1"}, "reliability": {"level": "R0"}},
|
||||
}
|
||||
entry = "---\n" + yaml.safe_dump(metadata) + "---\nPrivate fixture body\n"
|
||||
index = yaml.safe_dump({"capabilities": [{
|
||||
"id": cap_id, "name": "Fixture", "summary": "Fixture metadata", "vector": "D3 / A2 / C1 / R0",
|
||||
"domain": "infotech", "status": "draft", "owner": repo, "path": path,
|
||||
}]})
|
||||
index_url = verifier._raw_url(repo, commit, "registry/indexes/capabilities.yaml")
|
||||
bodies[index_url] = index
|
||||
bodies[verifier._raw_url(repo, commit, path)] = entry
|
||||
sources.append({"repo": repo, "capability_ids": [cap_id], "consented_commit": commit,
|
||||
"native_raw_url": index_url, "index_sha256": _digest(index), "auth_env": verifier.AUTH_ENV})
|
||||
approved.append({"repo": repo, "capability_ids": [cap_id], "commit": commit, "files": [
|
||||
{"path": "registry/indexes/capabilities.yaml", "sha256": _digest(index)},
|
||||
{"path": path, "sha256": _digest(entry)},
|
||||
]})
|
||||
disclosure = json.dumps({"id": "fixture", "revision": 1, "sources": approved})
|
||||
consent = json.dumps({"consent_status": "approved", "proposal_sha256": _digest(disclosure),
|
||||
"proposal_id": "fixture", "proposal_revision": 1, "repositories": sorted(verifier.REPOSITORIES)})
|
||||
proposal = json.dumps({"sources": sources})
|
||||
paths = []
|
||||
for name, text in (("proposal", proposal), ("disclosure", disclosure), ("consent", consent)):
|
||||
path = tmp_path / f"{name}.json"
|
||||
path.write_text(text)
|
||||
paths.append(path)
|
||||
monkeypatch.setattr(verifier, f"{name.upper()}_SHA256", _digest(text))
|
||||
return paths, bodies
|
||||
|
||||
|
||||
def _transport(monkeypatch, bodies, mutate=None):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, body):
|
||||
self.body = body
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return self.body.encode()
|
||||
|
||||
class FakeOpener:
|
||||
def open(self, req, timeout):
|
||||
assert req.get_header("Authorization") == "Bearer fixture-secret"
|
||||
assert timeout == 30
|
||||
calls.append(req.full_url)
|
||||
body = bodies[req.full_url]
|
||||
return FakeResponse(mutate(req.full_url, body) if mutate else body)
|
||||
|
||||
monkeypatch.setenv(verifier.AUTH_ENV, "fixture-secret")
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda handler: FakeOpener())
|
||||
# Deliberately prohibit composition/cache resolution; direct fetch must work.
|
||||
monkeypatch.setattr(federation, "resolve_source_index_path", lambda *a, **k: pytest.fail("cache path used"))
|
||||
return calls
|
||||
|
||||
|
||||
def test_exact_consented_native_index_and_entries_no_cache(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
calls = _transport(monkeypatch, bodies)
|
||||
receipt = verifier.verify(*paths)
|
||||
assert receipt["all_verified"]
|
||||
assert not receipt["cache_used"]
|
||||
assert len(calls) == 14
|
||||
assert all("?ref=" + "a" * 40 in url for url in calls)
|
||||
output = json.dumps(receipt)
|
||||
assert "fixture-secret" not in output
|
||||
assert "Private fixture body" not in output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("suffix", [".yaml", ".md"])
|
||||
def test_changed_unconsented_body_fails(packets, monkeypatch, suffix):
|
||||
paths, bodies = packets
|
||||
_transport(monkeypatch, bodies, lambda url, text: text + "changed" if suffix in url else text)
|
||||
receipt = verifier.verify(*paths)
|
||||
assert not receipt["all_verified"]
|
||||
assert all(not row["verified"] for row in receipt["sources"])
|
||||
|
||||
|
||||
def test_packet_tampering_rejected_before_fetch(packets, monkeypatch):
|
||||
paths, _ = packets
|
||||
paths[0].write_text(paths[0].read_text() + " ")
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", lambda *a: pytest.fail("network before consent checks"))
|
||||
assert verifier.verify(*paths)["error"] == "packet_scope_or_schema_validation_failed"
|
||||
|
||||
|
||||
def test_missing_auth_fails_before_transport(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
monkeypatch.delenv(verifier.AUTH_ENV, raising=False)
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: pytest.fail("transport without auth"))
|
||||
receipt = verifier.verify(*paths)
|
||||
assert not receipt["all_verified"]
|
||||
assert all(not row["verified"] for row in receipt["sources"])
|
||||
|
||||
|
||||
def test_error_output_never_embeds_secret_or_body(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
|
||||
def failure(*args):
|
||||
raise ValueError("fixture-secret Private fixture body")
|
||||
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", failure)
|
||||
output = json.dumps(verifier.verify(*paths))
|
||||
assert "fixture-secret" not in output
|
||||
assert "Private fixture body" not in output
|
||||
|
||||
|
||||
def test_broadened_runtime_reader_scope_fails_before_fetch(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies) + ["https://attacker.invalid/"]}))
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", lambda *a: pytest.fail("broadened scope used"))
|
||||
assert not verifier.verify(*paths)["all_verified"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("target", ["https://attacker.invalid/stolen", "https://forgejo.coulomb.social/other/repo"])
|
||||
def test_native_auth_redirect_never_replays_header(monkeypatch, target):
|
||||
calls = []
|
||||
|
||||
class FakeHTTPS(HTTPSHandler):
|
||||
def https_open(self, req):
|
||||
calls.append(req.full_url)
|
||||
headers = Message()
|
||||
headers["Location"] = target
|
||||
result = addinfourl(io.BytesIO(b""), headers, req.full_url, 302)
|
||||
result.msg = "Found"
|
||||
return result
|
||||
|
||||
# Use real urllib opener/redirect/error processors and fake HTTPS transport.
|
||||
opener = build_opener(FakeHTTPS(), federation._RejectAuthenticatedRedirect())
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: opener)
|
||||
monkeypatch.setenv("FIXTURE_AUTH", "fixture-secret")
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({"FIXTURE_AUTH": ["https://forgejo.coulomb.social/approved"]}))
|
||||
with pytest.raises(ConnectionError, match="redirect refused"):
|
||||
federation.fetch_remote_index_text("https://forgejo.coulomb.social/approved", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
assert calls == ["https://forgejo.coulomb.social/approved"]
|
||||
|
||||
|
||||
def test_authenticated_http_rejected_before_transport(monkeypatch):
|
||||
monkeypatch.setenv("FIXTURE_AUTH", "fixture-secret")
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: pytest.fail("cleartext transport"))
|
||||
with pytest.raises(ValueError, match="HTTPS"):
|
||||
federation.fetch_remote_index_text("http://forgejo.coulomb.social/approved", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("config", [None, "broken-json", "[]", '{"FIXTURE_AUTH":"https://attacker.invalid/"}', '{"OTHER_AUTH":["https://attacker.invalid/"]}'])
|
||||
def test_unapproved_auth_target_rejected_before_credential_lookup(monkeypatch, config):
|
||||
if config is None:
|
||||
monkeypatch.delenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", config)
|
||||
monkeypatch.setattr(federation, "_auth_headers", lambda *a: pytest.fail("unauthorized credential lookup"))
|
||||
with pytest.raises(ValueError, match="runtime-authorized"):
|
||||
federation.fetch_remote_index_text("https://attacker.invalid/", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1",
|
||||
"checked_at": "2026-10-14T22:43:42.958330+00:00",
|
||||
"proposal_sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99",
|
||||
"disclosure_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35",
|
||||
"consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
"cache_used": false,
|
||||
"all_verified": true,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"9de890da536c34ac2b50576e2c4a9c03792c8a07545d2a0566ace68ac10a293e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"21415ed5e89dcf8bd2df0c8bc99b07d2c87173809c37fa2582de6d336918a126"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"64503872abb24bb28f291264e860ab094c6abb721fbfe90b47258ad13b77af44"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"1a15e37bd3d19c4c89ef25803667937f549ade1cd27bedfa85132ac48ffe3f48"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"a67dbf6905223c04b1c003d5f967841038743c8c1479812f5cf0bb55ad6cee77"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"e7cf0485cfdb6f497281638d81f908b5c2fe1053125c6ef28130504909d1d49e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"84ec4fdce99134c88c110104cac93bdea36cffb5ba34e046b9ad161fb860dce2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"transport": "fake-only local committed metadata; no authenticated network fetch or runtime evidence"
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
# Reuse-surface independent consumer preparation review
|
||||
|
||||
Disposition: **changes_requested** for reader provisioning proposal
|
||||
`CUST81-PRIVATE-READER-PROVISIONING-PREPARATION-R1`, SHA256
|
||||
`7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99`.
|
||||
Reviewer: delegated reuse-surface owner preparation worker, supervised local scope.
|
||||
This review does not authorize credentials, approve another owner's implementation,
|
||||
claim deployment success, or grant permanent repo authority.
|
||||
|
||||
INTENT requires inspectable registry membership and evidence-driven consumer
|
||||
reliability. Acquisition must protect source custody while making the explicitly
|
||||
consented metadata discoverable. The proposal's seven-repository reader, separate
|
||||
KV path and exact commit pins fit that purpose, but its unchanged-image rollout
|
||||
assumption fails consumer credential isolation.
|
||||
|
||||
## Findings and prepared correction
|
||||
|
||||
1. Native `fetch_remote_index_text` originally supplies Authorization to urllib's
|
||||
default opener. Redirect handling copies headers to the redirect target. A
|
||||
source redirect can disclose the PAT to another origin or unreviewed path.
|
||||
Prepared patch rejects **all authenticated redirects**, even same-origin ones.
|
||||
Anonymous federation keeps its previous behavior.
|
||||
2. Hub registration accepts caller-selected `auth_env`, `auth_header` and `url`.
|
||||
Composition passes these to `_auth_headers`, which reads the named runtime env
|
||||
and sends its value to that URL. A holder of the writer token can therefore
|
||||
induce credential exfiltration without a redirect. Prepared patch validates
|
||||
operator-owned `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST` JSON before looking up the
|
||||
credential. Exact allowed env name and URL are mandatory; missing/malformed
|
||||
configuration fails closed. Authenticated HTTP is rejected.
|
||||
3. `resolve_source_index_path(refresh=True)` may return stale cache after remote
|
||||
failure. A fresh composition timestamp alone cannot prove private acquisition.
|
||||
Prepared helper calls native fetch directly, never the resolver/cache path,
|
||||
and verifies exact consented index **and Markdown entry** SHA256 plus capability
|
||||
identity, owner and entry schema. Failed receipts contain bounded categories,
|
||||
never raw exception text, bodies, headers, or secrets.
|
||||
|
||||
## Exact prepared artifacts
|
||||
|
||||
- `reuse_surface/federation.py` SHA256
|
||||
`dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd`
|
||||
- `reuse_surface/private_source_verify.py` SHA256
|
||||
`c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c`
|
||||
- `tests/test_private_source_verify.py` SHA256
|
||||
`5f0c0b30b98b4db96d695bdda952b25707916e297d566744fadb6dde7bea18b2`
|
||||
- `runtime-auth-source-allowlist-r1.json` SHA256
|
||||
`e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286`
|
||||
is the exact fourteen commit-pinned index/entry URLs for reader env
|
||||
`REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN`. Serialize this object as the runtime
|
||||
value of `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST`; do not use host wildcards.
|
||||
|
||||
## Validation and limits
|
||||
|
||||
Focused fake transport tests cover native Bearer header handling, all fourteen
|
||||
fetches, no cache path, mismatched index/entry hashes, tampered consent packet,
|
||||
missing authentication, sanitized errors, same/cross-origin redirects without
|
||||
second requests, HTTP rejection, missing/malformed/unapproved allowlists, and
|
||||
broadened reader URL scope. Focused tests: **30 passed**. Whole test suite initially
|
||||
reported **226 passed, 1 failed**: existing `test_federation_compose` resolved a
|
||||
required sibling index to missing `/tmp/access-engine/registry/indexes/capabilities.yaml`.
|
||||
The first sibling fixture preparation also stopped at maturity-engine's untracked
|
||||
registry index, and that partial fixture run reported **226 passed, 1 failed**.
|
||||
Both outcomes remain in `suite-environment-validation.json`; no consumer test
|
||||
failure or failed preparation is hidden.
|
||||
|
||||
With the complete task-owned fixture environment, the unchanged source patch
|
||||
passes the **entire suite: 227 passed, 1 existing dependency warning**. Command:
|
||||
`/home/worsch/reuse-surface/.venv/bin/python -m pytest -q tests`, cwd
|
||||
`/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface`. That fixture contains sixty
|
||||
required local registry indexes and thirty-nine seeded anonymous caches to avoid
|
||||
live network dependencies. Fifty-nine local indexes are copied from committed
|
||||
HEAD; maturity-engine's existing untracked index is copied read-only and labeled
|
||||
as a test fixture, never publication evidence. Member indexes are only read;
|
||||
composition writes its own copied registry/cache. No foreign checkout is written.
|
||||
Tests rewrite generated catalog/graph files in their own fixture checkout; earlier
|
||||
generated changes in the integration checkout were restored to its HEAD.
|
||||
|
||||
`local-consented-metadata-validation.json` proves schema and exact hashes for all
|
||||
seven repositories' consented committed metadata using a fake transport and a
|
||||
fixture-only token. It is explicitly **not** a network, credential, rollout or
|
||||
live federation verification receipt.
|
||||
|
||||
Static `registry/federation/sources.yaml` contains no `auth_env`, but public hub
|
||||
registration responses intentionally omit it and do not prove live SQLite dynamic
|
||||
source inventory. This patch changes authenticated-source behavior: every existing
|
||||
authenticated lane needs a reviewed exact-URL allowlist entry. Before rollout,
|
||||
platform/apps must safely inventory only auth env names and registered URLs,
|
||||
preserve approved existing lanes in runtime config, and prove no unrelated runtime
|
||||
secret name can be routed. No raw env dump or Secret read is required for that
|
||||
inventory. Until this migration and the reviewed new consumer image are complete,
|
||||
do not deliver the new PAT to the existing hub image.
|
||||
|
||||
After separate exact credential/rollout approval, invoke the helper from inside
|
||||
the hub process environment with the pinned proposal/disclosure/consent files:
|
||||
|
||||
```sh
|
||||
python -m reuse_surface.private_source_verify --proposal /review/private-reader-provisioning-preparation-r1.json --disclosure /review/private-source-disclosure-proposal-r1.json --consent /review/private-source-owner-consent-r1.json
|
||||
```
|
||||
|
||||
It accepts no credential argument and returns only bounded metadata receipts.
|
||||
Exit zero means all seven native index/entry fetches matched pinned consent; it
|
||||
does not prove registration/federation completion. Final admission still needs
|
||||
separate live source registrations and exact 70-ID fresh hosted audit.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN": [
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,628 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-SUITE-ENVIRONMENT-R1",
|
||||
"fixture_checkout": "/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface",
|
||||
"fixture_only": true,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "access-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7d59022dac9da4d8dcbecab936d3cea9b154c8c51478b378932fd9ebe5ec7e02"
|
||||
},
|
||||
{
|
||||
"repo": "activity-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "536d359366ccf3db3b2b0d390635bc2c5311bd37ac7ff89dae11d809ef15b848"
|
||||
},
|
||||
{
|
||||
"repo": "agentic-resources",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "42986b7a274ef510166e566a9756d594a6f88706189bc97ac889bab86b20240d"
|
||||
},
|
||||
{
|
||||
"repo": "approval-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b6812fd12dc530270fb66bae592c828f56f17f2fc86759cb04d750162fc619a4"
|
||||
},
|
||||
{
|
||||
"repo": "artifact-store",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "200802ac737195721a52d6d345104f2302fbdc35b4122b431bd2255f4e5552bc"
|
||||
},
|
||||
{
|
||||
"repo": "audit-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6a1d7dbb442f0fd0e481d54fb0acbee37ef2e693198b6f2108e154d3c3795e0e"
|
||||
},
|
||||
{
|
||||
"repo": "can-you-assist",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ce106e5c106453635075dd1b5906762d516e84601af4d5620c9a7faab444be46"
|
||||
},
|
||||
{
|
||||
"repo": "citation-engine",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e02914ffe937c8342022d92b5bfa6906aef560edeb6e90f80826cbf8e0620ada"
|
||||
},
|
||||
{
|
||||
"repo": "citation-evidence",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "628e338df6da2f0966675e8facf91f52cc3ac4a0c81417b33e1284045db1dfad"
|
||||
},
|
||||
{
|
||||
"repo": "citation-work",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "commerce-canon",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6e1186aad49634b9725bc2cfa051c42b2ac13e5c4abe4bade7b39c0483f4242d"
|
||||
},
|
||||
{
|
||||
"repo": "config-atlas",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d0325d71a5de1e756c080dc9abc3162536b9700cc37942a028ad2f65bad9bfef"
|
||||
},
|
||||
{
|
||||
"repo": "coordination-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f7a2c140ac1a7628a2106b1047b59e208879257b23ffed6dceef8ecf6c3bd420"
|
||||
},
|
||||
{
|
||||
"repo": "core-hub",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "31cb09dd3b04a7891533387479ecb340b543445b64a849d9973ee857efa4cca1"
|
||||
},
|
||||
{
|
||||
"repo": "domain-tree",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "email-connect",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5bce1e037a368816811a21dd9123ebf47f1a7d293c424c661d30fa14f2366231"
|
||||
},
|
||||
{
|
||||
"repo": "emergent-security",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "268d9192dc9b97da462d46fe5b40ea33e678840b23944ab0cd73622c130bdef6"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-anchor",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-binder",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2664dfb5668238051b7cd540470ee2f7a5557415c53462fa4f14474829f3ce46"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-source",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7b2a3c7b108fbdc69828b088915418c87dedfee5e202e4546defe68d8df64667"
|
||||
},
|
||||
{
|
||||
"repo": "feature-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2335b614d120a3e954d55389448b4c8462bfab3582de2e0bbae02dd5474dd132"
|
||||
},
|
||||
{
|
||||
"repo": "fin-hub",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c6bb1aa6b770f684721fae9f1576f2c2f0128d1b758f4e33b8a277e0b4ff087a"
|
||||
},
|
||||
{
|
||||
"repo": "gate-house",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2b63d94375d29649c41a7eae7cfb3f6e35210fd3438aa5013f3ddc22a662511a"
|
||||
},
|
||||
{
|
||||
"repo": "glas-harness",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8f86dbdaf6535c909ff19967f7806d85c1f7a09db1e999033abf9b9007ed805b"
|
||||
},
|
||||
{
|
||||
"repo": "guide-board",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "abbc20c7ab14bcbdb73679a5143355c7ad3a7441b8f8e7bf5a1af393c7d1c1e1"
|
||||
},
|
||||
{
|
||||
"repo": "helix-forge",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "hub-core",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a78d185973e0d0600f6ff05470ff3b840130fca4ed7922edaf08155d246e9008"
|
||||
},
|
||||
{
|
||||
"repo": "human-resources",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d72d6b4f1479992e33400ed7c950cdc7112ead9c22abd38275717f0ae9255787"
|
||||
},
|
||||
{
|
||||
"repo": "ihp-railiance-probe",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "info-tech-canon",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6015ec78772586e6e351422741430be8c40d7c7116403ee4b92933df342c4c72"
|
||||
},
|
||||
{
|
||||
"repo": "informed-decision",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b43ea44c725dcbd59fe957ffc307805e0aabc23b60a4a9049005c70ca4871a62"
|
||||
},
|
||||
{
|
||||
"repo": "infospace-bench",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "74ba65dad84faffd6ce0e6d771f8650abe76b87a6ee09ee007780bd13c5cd9b2"
|
||||
},
|
||||
{
|
||||
"repo": "issue-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "70eb00870ed87072aeb0f7c38de0a879f1b3910fe6f976b982a191e261e8e7be"
|
||||
},
|
||||
{
|
||||
"repo": "kaizen-agentic",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0236d486d6df6d928e7fd9f186a02273c8a59d534abb026603f7128876fbe776"
|
||||
},
|
||||
{
|
||||
"repo": "key-cape",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "901d15c15cacd844cf19a1a7b74c890ce5e0b00a3372a9e10bd7114f328596c8"
|
||||
},
|
||||
{
|
||||
"repo": "kings-guard",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8a143864a6ce9a7b6c5a0f64eb75f67cfa99c2b9b155baac4f81a261d3a5312c"
|
||||
},
|
||||
{
|
||||
"repo": "kontextual-engine",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5a5980f37031c15fc9b332f143ddbfbd6c2789ff6c6ec0f4c3e971cbc506830e"
|
||||
},
|
||||
{
|
||||
"repo": "llm-connect",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "27420d2b21fb45cf6cf6ac41890518f6b5a8c10027594a7e95ec535715a01296"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-filter",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "95d2743d4201f6b89efedebfcd34fdb03e3f832fa00b6f4f646015c3264dfb60"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-main",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ba7e29cc82fd4d1eff61e48b1d870714f2d9c97531e839c469f892602509f137"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-quarkdown",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "67c718d9bb8221b9b58ab30cbf9eb1cf340e2d2ecc1d77cb4b5bdaf28fa5befc"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-tool",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "9159a4af2783b17602c23ca4b72b552641d5e1c3170a4dee1aee69c3ba9371d2"
|
||||
},
|
||||
{
|
||||
"repo": "maturity-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "worktree_untracked_registry_fixture_not_publication_evidence",
|
||||
"sha256": "42b4cd1f9c6fe0960acadd10a337a579a6313fa46c2ff5a2a34bbacf2749d049"
|
||||
},
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6fadeb0e89729a9226d6eebd44fbb07723512a3ebe6737f5a1113b14829447ed"
|
||||
},
|
||||
{
|
||||
"repo": "open-cmis-tck",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e8fe7a7d0d3af104009f996c466353467e737069d0c2d04f880a86ae60d76bda"
|
||||
},
|
||||
{
|
||||
"repo": "open-reuse",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f5e8b9cd3ceb7e336a2afc5097065a6c0453cc697c8c4aa2d2c39c1d1fe4fe66"
|
||||
},
|
||||
{
|
||||
"repo": "ops-bridge",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "aefd795eb16fcfdebc108450826a0f62e000bc61e9d78007bc6221bc2328e40c"
|
||||
},
|
||||
{
|
||||
"repo": "ops-hub",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6fea9fee803a5d55ece3daefdfda0efff11eae8d2a0b719fbd0dec64f4f85314"
|
||||
},
|
||||
{
|
||||
"repo": "ops-mason",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5eb499bc819c874835b8e6727ccbd5f9889ebb6f25a90018e3e574e5b6082b32"
|
||||
},
|
||||
{
|
||||
"repo": "ops-warden",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4d3aba70ead10c826814dc766f9d23e3e0ce52475330acf57905bf62b01c819c"
|
||||
},
|
||||
{
|
||||
"repo": "phase-memory",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e84b7603c98ae48879002b16a8302fb0e0e1b6c6e1099565570a8d8f98478d31"
|
||||
},
|
||||
{
|
||||
"repo": "policy-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c1eb3e23621de354a9081cbf29a2f3d772b5f8538aaceca417574cde5a04c021"
|
||||
},
|
||||
{
|
||||
"repo": "rail-knative",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e044b8f2f6bc1b8aa7c672ee33dfac69d6813fd672388713a70ab7ea4d4052c0"
|
||||
},
|
||||
{
|
||||
"repo": "rail-kubernetes",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "699f69facea1ce4e71d90fe0790db19b68d3a546015453651d3afc808d31d09d"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-apps",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2a550acf8545e95bd81842bb1b00f4db46c4dbb42b522823f043e948667ddf2c"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-bootstrap",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "feefcc699e045a1a72f254d71f46460ad2362ed23c9de6e54c952711fcb33788"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-cluster",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0e67a2aca183ee7ef9c4c04e618fcb03647120f456a133e84a8f42c2b8d8fcde"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-enablement",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4b1f91b0da94e3098916f3e360b20b9926be4dd8683d6fb167a027aaf04ebfac"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-fabric",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f473644906fe3f331702b407c3519e6a3cd0d96d362cf30d80e03aac53c3b862"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-forge",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4df605d617b7656ffb94a43ab4633c8fc69ec723f68f5f5b8d5f24ad8efb6b98"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-infra",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "14a76b1f1dd317a1d426502ff5f80780e9551cd1cd600543de6fb9a84cb8ada2"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-master",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f36cb2765ec04064314f43866f9a87a94a4f0f728adfaad46739f232b4986c0a"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0315d526165368e0a513de6b15932778d43c3e6045c589c95060f555fca3202e"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-telemetry",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "82f3f2d188e2d7ebea71b7262e106c5f48feaf39a2da881bb645ff9352884205"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-playwright",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "79645df817858f96230f2443d66796d1078185c7afef5948f6a38632234d33bb"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc"
|
||||
},
|
||||
{
|
||||
"repo": "reef-railiance",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "84a89c376021e3cb980c02c41d1a31396b874bebcc3801c2d1a9a33ae4195eb5"
|
||||
},
|
||||
{
|
||||
"repo": "rein-aharness",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "20990ed0e016b3330a0d44e9b86bfff8966e82bc8800469ec0f320cfa8a63fbc"
|
||||
},
|
||||
{
|
||||
"repo": "rein-openweights",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2f4e59b2dbc7ea6078405f5c2f2be0aebd9434bee171a429b9ebe6baa83f3c2b"
|
||||
},
|
||||
{
|
||||
"repo": "repo-manager",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ba068a8dd7a282664e94e2acdbbb170a2ba48e260e7ce0ec8eaaa8aee6f9e7c2"
|
||||
},
|
||||
{
|
||||
"repo": "repo-scoping",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "480d7c57759d6997d7bd531e966c207bb290e978f9fe831d6d246b5592c3dd71"
|
||||
},
|
||||
{
|
||||
"repo": "repo-seed",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8cb4e4baacd95a070a96db6d72f4fb3019cffda95606c65896f796ccac0c9c7c"
|
||||
},
|
||||
{
|
||||
"repo": "resource-control",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "31b16f7d824e376f21de44aff46d5d532d3679d5e72035ce84813572b11bbd44"
|
||||
},
|
||||
{
|
||||
"repo": "reuse-surface",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a77c952e9e1a37a3504ccd7001a7a2493d60d46f147f277071829cd05646e31c"
|
||||
},
|
||||
{
|
||||
"repo": "risk-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "53f0bf9e0aa06a6db0aa991f0ebdaa1d2bf74cfaf4e30eed8c003a4d2186a746"
|
||||
},
|
||||
{
|
||||
"repo": "role-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "1188e02e0333664b3554d11c59c12942fef9e29e6491b186dcec67e6ae3e8a5c"
|
||||
},
|
||||
{
|
||||
"repo": "runbook-tutorials",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a74fa708c4fea755f5c52504bd227393888cca78c043349cdc182c4d783390e5"
|
||||
},
|
||||
{
|
||||
"repo": "sand-boxer",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a182d5672d088e80b79eacff9cb37fc2f3518c3c100537e18d125abc7f169e96"
|
||||
},
|
||||
{
|
||||
"repo": "sbom-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ed6efd581483d0058a3928b365b933cbbd98c035b0a75c866156a69961d74edc"
|
||||
},
|
||||
{
|
||||
"repo": "secrets-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d5d903336d05ccd0b75a49f7be7e866ef89c6bdb6444e8c747a5ffa934a6bbe2"
|
||||
},
|
||||
{
|
||||
"repo": "security-canon",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "9b9f9348e560a72d8032f62b2ba4820d81fe182353ccc886e4f5c57cbdea563f"
|
||||
},
|
||||
{
|
||||
"repo": "shard-wiki",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b0bb63c0a080e46c60f24a87959c529da5e725334ed69c4ba985c39107c54ef9"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "eab454bd985ab6ab83c38fd29e6e58ebdfe1e09bbc5388edddaa07a88133619a"
|
||||
},
|
||||
{
|
||||
"repo": "tegwick-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "tenant-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4131f1b63e52306f267204cdfb0faa90d6550f8bca51f97cf1062becea95d805"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "cfe7f25e91fee824347aa22ccee2a70e7dd58ac06a7106f7e2ec9e54dc163020"
|
||||
},
|
||||
{
|
||||
"repo": "user-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7133c52c2013cea70d07d33960f145c370d33a8b4397c1dc927769fcaf200cbd"
|
||||
},
|
||||
{
|
||||
"repo": "vantage-point",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "cc5cf52ead95adc2920db84299d994e54def089d3f8fd852e10d9857ab0fdd2e"
|
||||
},
|
||||
{
|
||||
"repo": "vergabe-teilnahme",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "73ffd306894d9839f4800aa028144883d012bf0fe74a310ad09ef9296c627d56"
|
||||
},
|
||||
{
|
||||
"repo": "whitehat-security",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e4bf9bac7f62f60cf10a9fbd25aa8200c22f13d61861862ff5fa1cc90b55f37c"
|
||||
},
|
||||
{
|
||||
"repo": "whynot-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "whynot-design",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "541104bcafb17dcf7d26dabbce7ff93b2025a0bc6cad9bec71ac145f5ec72669"
|
||||
},
|
||||
{
|
||||
"repo": "zone-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b6b196089a3011f16e8a38f6914589efc72a29ee3aca4bc0c5e3cf30bee97ec4"
|
||||
}
|
||||
],
|
||||
"foreign_repo_writes": false,
|
||||
"network_fetch_needed": false,
|
||||
"preserved_prior_outcomes": [
|
||||
{
|
||||
"environment": "original isolated checkout without siblings",
|
||||
"passed": 226,
|
||||
"failed": 1,
|
||||
"failure": "required access-engine sibling missing"
|
||||
},
|
||||
{
|
||||
"environment": "partial fixture preparation failed at untracked maturity-engine registry",
|
||||
"passed": 226,
|
||||
"failed": 1,
|
||||
"failure": "required maturity-engine fixture missing; preparation failure not hidden"
|
||||
}
|
||||
],
|
||||
"final_result": {
|
||||
"command": "/home/worsch/reuse-surface/.venv/bin/python -m pytest -q tests",
|
||||
"cwd": "/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface",
|
||||
"exit_code": 0,
|
||||
"passed": 227,
|
||||
"failed": 0,
|
||||
"warnings": 1,
|
||||
"duration_seconds": 7.8,
|
||||
"warning": "Starlette httpx testclient deprecation",
|
||||
"consumer_source_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue