From 5b2435cb58e34f52ca77440ce409580bb38eeb66 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 15:44:44 +0200 Subject: [PATCH] docs: reef attribute refs and resource object workplan Publish the operational reference convention (reef: and secret: handles). Add a resource-control view of reef-railiance that excludes S3. Open RESOURCE-WP-0006 for the five-facet inventory model. --- INTENT.md | 8 +- SCOPE.md | 1 + WORK-RECORDS.md | 5 + data/reefs/reef-railiance.json | 35 +++++ docs/operational-reference-convention.md | 146 ++++++++++++++++++ schemas/reef-view.schema.json | 42 +++++ tests/test_portfolio.py | 16 ++ tools/validate.py | 12 ++ ...WP-0002-procure-postgres-backup-storage.md | 7 + ...E-WP-0006-resource-object-and-reef-refs.md | 124 +++++++++++++++ 10 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 data/reefs/reef-railiance.json create mode 100644 docs/operational-reference-convention.md create mode 100644 schemas/reef-view.schema.json create mode 100644 workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md diff --git a/INTENT.md b/INTENT.md index d32c6de..c0728ba 100644 --- a/INTENT.md +++ b/INTENT.md @@ -149,7 +149,13 @@ resources and utilization; the booked financial fact remains authoritative in 10. **Avoid correlated failure silently.** Shared failure domains may be intentional, but they and their compensating controls must be explicit. 11. **Credentials stay elsewhere.** Provider keys and billing credentials live - in approved secret-custody lanes, never in this repository. + in approved secret-custody lanes, never in this repository. Inventory + stores `secret:` handles only. +12. **Operating attributes stay on the reef.** Endpoints, buckets, member + lists, and kubeconfig paths are cited as `reef:` references + (`docs/operational-reference-convention.md`). This repository does not + operate the resource. Compute substrate: `reef-railiance`. Object-store + substrate for backup: `reef-storage`, not an add-on to the home reef. ## What it does not own diff --git a/SCOPE.md b/SCOPE.md index 273a516..18100c0 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -47,5 +47,6 @@ resource-control exists to provide the capability described in INTENT.md. - Start with: INTENT.md - Internal procurement terms: docs/TermsOfResourceProcurement.md +- Operational refs: docs/operational-reference-convention.md - Agent instructions: AGENTS.md - Workplans: workplans/ diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index a07d974..821b8cb 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -13,6 +13,7 @@ | workplan | RESOURCE-WP-0003 | finished | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md | | workplan | RESOURCE-WP-0004 | finished | — | workplans/RESOURCE-WP-0004-financial-entity-procurement-terms.md | | workplan | RESOURCE-WP-0005 | finished | — | workplans/RESOURCE-WP-0005-resource-procurement-facility.md | +| workplan | RESOURCE-WP-0006 | ready | — | workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md | | task | RESOURCE-WP-0001-T01 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md | | task | RESOURCE-WP-0001-T02 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md | | task | RESOURCE-WP-0001-T03 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md | @@ -42,3 +43,7 @@ | task | RESOURCE-WP-0005-T05 | done | — | workplans/RESOURCE-WP-0005-resource-procurement-facility.md | | task | RESOURCE-WP-0005-T06 | done | — | workplans/RESOURCE-WP-0005-resource-procurement-facility.md | | task | RESOURCE-WP-0005-T07 | done | — | workplans/RESOURCE-WP-0005-resource-procurement-facility.md | +| task | RESOURCE-WP-0006-T01 | todo | — | workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md | +| task | RESOURCE-WP-0006-T02 | progress | — | workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md | +| task | RESOURCE-WP-0006-T03 | todo | — | workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md | +| task | RESOURCE-WP-0006-T04 | todo | — | workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md | diff --git a/data/reefs/reef-railiance.json b/data/reefs/reef-railiance.json new file mode 100644 index 0000000..0b0845c --- /dev/null +++ b/data/reefs/reef-railiance.json @@ -0,0 +1,35 @@ +{ + "schema_version": "0.1", + "reef_id": "reef-railiance", + "repo": "reef-railiance", + "declaration_ref": "reef:railiance/declarations/reef.yaml", + "procuring_entity_id": "entity:railiance", + "financial_entity_id": "entity:railiance", + "role": "compute_substrate", + "resources": [ + {"resource_id": "resource:hosteurope:railiance01", "role": "member_capacity"}, + {"resource_id": "resource:railiance:reef-railiance:k3s", "role": "hosted_runtime"}, + {"resource_id": "resource:railiance:apps-pg", "role": "hosted_service"}, + {"resource_id": "resource:railiance:forgejo", "role": "hosted_service"}, + {"resource_id": "resource:tenant:coulomb:coulomb-social", "role": "hosted_service"}, + {"resource_id": "resource:tenant:friendly:binky:rapp-qonto", "role": "hosted_service"} + ], + "consumers_potential": [ + "rapp-postgres", + "rapp-qonto", + "rapp-openbao", + "coulomb-social", + "helix-forge", + "railiance-platform" + ], + "consumers_actual": [ + "coulomb-social", + "rapp-qonto", + "railiance-platform" + ], + "notes": [ + "This is a resource-control cost and consumption view, not a copy of reef.yaml.", + "Member IPs, SSH, kubeconfig, and rail admission stay in reef-railiance.", + "S3-compatible backup storage is out of this reef; that substrate is reef-storage." + ] +} diff --git a/docs/operational-reference-convention.md b/docs/operational-reference-convention.md new file mode 100644 index 0000000..ef4a88b --- /dev/null +++ b/docs/operational-reference-convention.md @@ -0,0 +1,146 @@ +# Operational reference convention v0.1 + +Status: baseline +Effective: 2026-08-14 +Owners: `resource-control` (join keys), `reef-*` (operational facts) + +`resource-control` manages resources from **procurement, consumption, usage, +and cost**. It does not operate them. A `reef-*` repo is the operational +substrate. Non-secret operating attributes and credential *handles* are +therefore **references**, never copies of the operating record and never +secret values. + +This is the join convention. Schema adoption is `RESOURCE-WP-0006`. +`reef-storage` materialization is `RMASTER-WP-0022`. + +--- + +## Authority + +| Fact | Authoritative home | +| --- | --- | +| Why the resource exists, what was decided, who pays, who may consume, what it cost | `resource-control` | +| How the substrate is named, reached, bound, and run | the `reef-*` repo | +| Secret material (access keys, tokens, payment instruments) | approved OpenBao / credential-broker lane (`railiance-platform` today) | +| Booked invoices and recognized payments | `fin-hub` | +| Workload use of a reviewed destination | the consuming `rapp-*` | + +Do not duplicate a reef attribute into the inventory record “for convenience.” +Cite it. If the reef path moves, update the reference, not a shadow copy. + +--- + +## Reference forms + +### Reef attribute + +A non-secret operating fact owned by a reef: + +```text +reef:/# +``` + +- `reef_id` equals the reef repo slug (`reef-railiance`, `reef-storage`). +- Path is from the reef repo root, POSIX, no `..`. +- Fragment is a dotted key into that file (`endpoint`, `bucket.prefix`). +- The file is committed, non-secret, and the reef is authoritative for it. + +Examples (illustrative until `reef-storage` exists): + +```text +reef:storage/substrate/object-stores/platform-audit-storage.yaml#endpoint +reef:storage/substrate/object-stores/platform-audit-storage.yaml#bucket +reef:storage/substrate/object-stores/platform-audit-storage.yaml#region +reef:railiance/declarations/reef.yaml#current_members +``` + +Inventory stores the reference, not the endpoint string. + +### Credential handle + +A pointer to custody, never a value: + +```text +secret:/ +``` + +Example already named by the backup split: + +```text +secret:railiance-platform/platform-pg-backup-s3 +``` + +The handle may also record `broker` (`openbao`) and `rotation` as non-secret +metadata. The secret body stays in the broker. + +### Structured form + +When a record needs more than a string: + +```json +{ + "kind": "reef_attribute", + "reef_id": "reef-storage", + "repo": "reef-storage", + "path": "substrate/object-stores/platform-audit-storage.yaml", + "key": "endpoint", + "ref": "reef:storage/substrate/object-stores/platform-audit-storage.yaml#endpoint" +} +``` + +```json +{ + "kind": "credential_handle", + "custody_repo": "railiance-platform", + "secret_id": "platform-pg-backup-s3", + "broker": "openbao", + "ref": "secret:railiance-platform/platform-pg-backup-s3" +} +``` + +Both `ref` and the parsed fields must agree when both are present. + +--- + +## What a resource-control resource then holds + +Five facets, all first-class in the forthcoming inventory v0.3: + +1. **Description** — purpose and class; why this resource exists. +2. **Decision** — chosen option, approver, date, rejected alternatives + (or an explicit “not decided” gap). Required before status `ordered`. +3. **Operational attribute refs** — `reef:…` pointers. Empty only with an + explicit gap (reef not created yet). +4. **Credential handles** — `secret:…` pointers. Empty is allowed when no + credential exists yet; values are never stored here. +5. **Consumers** — `potential` (intended / declared) vs `actual` (observed + using the resource). A name in `workload_ids` alone is not enough. + +--- + +## Reef views + +`data/reefs/.json` is the **resource-control view** of a reef: which +inventory records implement its procured capacity, which entities pay, which +consumers are potential vs actual. It is not a second `reef.yaml` and must +not restate member IPs, kubeconfig paths, or endpoints. + +`reef-railiance` is compute (Host Europe `railiance01`, k3s, platform +services). It does **not** grow an S3 object store. Backup object storage is +a different substrate boundary: `reef-storage`. + +--- + +## Backup / S3 split (the proving case) + +| Concern | Owner | +| --- | --- | +| Demand, comparison, purchase approval, cost, settlement | `resource-control` (`RESOURCE-WP-0002`) | +| Operate the Scaleway (or successor) object store: endpoint, bucket, region, lifecycle as committed facts | `reef-storage` (`RMASTER-WP-0022`) | +| Credential custody and delivery | `railiance-platform` OpenBao | +| Barman, schedule, retention, restore | `rapp-postgres` after a reviewed destination | +| Consume the destination | `rapp-postgres` (actual once WAL flows) | + +Until `reef-storage` exists, the proposed backup record may cite a gap +instead of a `reef:` URL. That gap is expected; inventing an endpoint here +is not. diff --git a/schemas/reef-view.schema.json b/schemas/reef-view.schema.json new file mode 100644 index 0000000..5126f09 --- /dev/null +++ b/schemas/reef-view.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://coulomb.social/resource-control/reef-view.schema.json", + "title": "Resource-control view of a reef v0.1", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "reef_id", + "repo", + "declaration_ref", + "procuring_entity_id", + "role", + "resources", + "consumers_potential", + "consumers_actual" + ], + "properties": { + "schema_version": {"const": "0.1"}, + "reef_id": {"type": "string", "pattern": "^reef-[a-z0-9]+(-[a-z0-9]+)*$"}, + "repo": {"type": "string", "pattern": "^reef-[a-z0-9]+(-[a-z0-9]+)*$"}, + "declaration_ref": {"type": "string", "pattern": "^reef:"}, + "procuring_entity_id": {"type": "string", "pattern": "^entity:[a-z0-9]+$"}, + "financial_entity_id": {"type": ["string", "null"], "pattern": "^entity:[a-z0-9]+$"}, + "role": {"enum": ["compute_substrate", "storage_substrate", "other_substrate"]}, + "resources": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["resource_id", "role"], + "properties": { + "resource_id": {"type": "string", "pattern": "^resource:"}, + "role": {"enum": ["member_capacity", "hosted_runtime", "hosted_service", "object_store", "other"]} + } + } + }, + "consumers_potential": {"type": "array", "items": {"type": "string"}}, + "consumers_actual": {"type": "array", "items": {"type": "string"}}, + "notes": {"type": "array", "items": {"type": "string"}} + } +} diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index b91f7d9..2af3c4f 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -77,5 +77,21 @@ class PortfolioTest(unittest.TestCase): ) +class ReefViewTest(unittest.TestCase): + def test_reef_railiance_view_cites_only_inventory_and_excludes_s3(self): + view = json.loads((ROOT / "data/reefs/reef-railiance.json").read_text()) + inventory = { + json.loads(path.read_text())["id"] + for path in (ROOT / "data/resources").glob("*.json") + } + self.assertEqual("reef-railiance", view["reef_id"]) + self.assertTrue(view["declaration_ref"].startswith("reef:")) + self.assertEqual("compute_substrate", view["role"]) + for row in view["resources"]: + self.assertIn(row["resource_id"], inventory) + self.assertNotIn("resource:platform:audit-storage", {row["resource_id"] for row in view["resources"]}) + self.assertTrue(any("reef-storage" in note for note in view["notes"])) + + if __name__ == "__main__": unittest.main() diff --git a/tools/validate.py b/tools/validate.py index e273b71..2c9c937 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -114,6 +114,18 @@ def main() -> int: assert planning["transfer_price"] == "240.00" assert planning["credit_headroom"] == "760.00" assert planning["financial_entity_id"] == "entity:coulomb" + reef_view_schema = load("schemas/reef-view.schema.json") + assert reef_view_schema["$schema"].endswith("2020-12/schema") + inventory_ids = {resource["id"] for resource in resources if resource.get("record_scope") == "inventory"} + for path in Path("data/reefs").glob("*.json"): + view = load(str(path)) + assert view["schema_version"] == "0.1" + assert view["declaration_ref"].startswith("reef:") + missing = [row["resource_id"] for row in view["resources"] if row["resource_id"] not in inventory_ids] + assert not missing, f"{path} cites unknown resources: {missing}" + if view["reef_id"] == "reef-railiance": + assert view["role"] == "compute_substrate" + assert any("reef-storage" in note for note in view.get("notes") or []) print("resource-control declarations: valid") return 0 diff --git a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md index cbcf5ad..de50eab 100644 --- a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md +++ b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md @@ -69,6 +69,13 @@ This workplan owns requirements, comparison, procurement evidence, resource registration, cost/utilization monitoring, and proof that the purchased resource works. +The purchased object is a five-facet resource (`RESOURCE-WP-0006`): +description, decision, `reef:` attribute refs, `secret:` handles, and +potential vs actual consumers. Non-secret operating attributes (endpoint, +bucket, region) will be owned by `reef-storage` (`RMASTER-WP-0022`), not +inlined here and not placed on `reef-railiance`. See +`docs/operational-reference-convention.md`. + - Human financial authority approves purchase and accepts contractual terms. - `railiance-platform` owns provider credential custody and the approved Kubernetes Secret/credential-broker delivery. diff --git a/workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md b/workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md new file mode 100644 index 0000000..4c34c94 --- /dev/null +++ b/workplans/RESOURCE-WP-0006-resource-object-and-reef-refs.md @@ -0,0 +1,124 @@ +--- +id: RESOURCE-WP-0006 +type: workplan +title: "Resource object model and reef operational references" +domain: financials +repo: resource-control +status: ready +owner: grok +topic_slug: railiance +created: "2026-08-14" +updated: "2026-08-14" +related: + - RESOURCE-WP-0002 + - RESOURCE-WP-0003 + - RESOURCE-WP-0005 + - RMASTER-WP-0022 +state_hub_workstream_id: "2108f67a-c1a9-4e53-b427-7011168b1fd2" +--- + +# RESOURCE-WP-0006 — resource object model and reef operational references + +## Goal + +Make the inventory record a **managed resource object** with five facets: + +1. description +2. decision +3. operational attribute **references** (`reef:…`) +4. credential **handles** (`secret:…`) +5. potential vs actual consumers + +`resource-control` stays the procurement, consumption, usage, and cost +plane. It does not operate the resource. Operating facts live on a reef. +The convention is `docs/operational-reference-convention.md`. + +`reef-railiance` is the compute substrate. S3-compatible backup storage is +**not** added there. `reef-storage` (RMASTER-WP-0022) will operate Scaleway +object storage and own the non-secret attributes this repo will cite. + +## Boundaries + +- Secret values stay in OpenBao. This repo stores handles only. +- Reef attribute values stay in the reef repo. This repo stores references + only. +- `RESOURCE-WP-0002` procures the backup bucket against this model; it does + not invent endpoints. +- `RMASTER-WP-0022` materializes `reef-storage`. This workplan only defines + the join. + +## T01 — Adopt the five-facet inventory schema + +```task +id: RESOURCE-WP-0006-T01 +status: todo +priority: high +state_hub_task_id: "55d3ebc2-51b7-49d1-8d0d-57ccc6379ae7" +``` + +Publish inventory schema v0.3 with first-class description, decision, +`operational_refs`, `credential_handles`, and `consumers.potential` / +`consumers.actual`. Keep v0.2 records valid until migrated, or migrate all +live records in the same change. Decision is required before status +`ordered`. + +Done when validators reject an `ordered` resource without a decision, reject +an inline secret or endpoint that should be a `reef:` / `secret:` ref, and +accept the proposed backup record with an explicit attribute-ref gap until +`reef-storage` exists. + +## T02 — Reef views and the reef-railiance projection + +```task +id: RESOURCE-WP-0006-T02 +status: progress +priority: high +state_hub_task_id: "f2b38140-3d73-45a2-a2ff-fea12733e7b0" +``` + +Keep `data/reefs/*.json` as the cost/consumption view of a reef. Validate +`data/reefs/reef-railiance.json` against +`schemas/reef-view.schema.json`. Do not copy IPs, kubeconfig, or endpoints. +Add a `reef-storage` view only after that repo exists. + +Done when `make test` validates reef views, every `resource_id` in the +reef-railiance view exists in inventory, and the view states that S3 backup +is out of `reef-railiance`. + +Started 2026-08-14: convention published; first reef-railiance view committed. + +## T03 — Migrate live inventory to the five facets + +```task +id: RESOURCE-WP-0006-T03 +status: todo +priority: high +state_hub_task_id: "da959cf1-cbd2-492c-95ff-b9ab78c16697" +``` + +Backfill description, decision-or-gap, consumer potential/actual, and +operational/credential refs or explicit gaps on every +`data/resources/` record. The proposed backup resource is the proving +case: consumers potential = `rapp-postgres`; actual = empty until WAL +flows; attribute refs wait on `reef-storage`; credential handle = +`secret:railiance-platform/platform-pg-backup-s3` once custody exists. + +Done when no live record relies on undifferentiated `workload_ids` as the +only consumer list. + +## T04 — Wire WP-0002 to the convention + +```task +id: RESOURCE-WP-0006-T04 +status: todo +priority: medium +state_hub_task_id: "a34fa806-ee34-4d3b-a823-2797fe260bbc" +``` + +Update `RESOURCE-WP-0002` so T03+ produce a resource object, not a v0.2 +card with a dangling endpoint. The decision record, reef-storage attribute +refs, and the OpenBao handle are the procurement outputs. `rapp-postgres` +remains a consumer, not the procurer. + +Done when WP-0002 cites this convention and will not accept an inventory +update that inlines Scaleway endpoint or keys.