From 9d11d2a043bd5c557039b628d33b73551617c724 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 15:53:30 +0200 Subject: [PATCH 1/5] feat: resource-control view of reef-storage Project the planned Scaleway backup bucket onto the new storage reef. --- data/reefs/reef-storage.json | 19 +++++++++++++++++++ tests/test_portfolio.py | 10 ++++++++++ 2 files changed, 29 insertions(+) create mode 100644 data/reefs/reef-storage.json diff --git a/data/reefs/reef-storage.json b/data/reefs/reef-storage.json new file mode 100644 index 0000000..745d5f8 --- /dev/null +++ b/data/reefs/reef-storage.json @@ -0,0 +1,19 @@ +{ + "schema_version": "0.1", + "reef_id": "reef-storage", + "repo": "reef-storage", + "declaration_ref": "reef:storage/declarations/reef.yaml", + "procuring_entity_id": "entity:railiance", + "financial_entity_id": "entity:railiance", + "role": "storage_substrate", + "resources": [ + {"resource_id": "resource:platform:audit-storage", "role": "object_store"} + ], + "consumers_potential": ["rapp-postgres"], + "consumers_actual": [], + "notes": [ + "Provider-delegated S3 (Scaleway). No rail.", + "Attribute values: reef:storage/substrate/object-stores/platform-audit-storage.yaml", + "Independent of reef-railiance." + ] +} diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index 2af3c4f..475d951 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -92,6 +92,16 @@ class ReefViewTest(unittest.TestCase): 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"])) + def test_reef_storage_view_is_delegated_object_store(self): + view = json.loads((ROOT / "data/reefs/reef-storage.json").read_text()) + self.assertEqual("storage_substrate", view["role"]) + self.assertEqual( + ["resource:platform:audit-storage"], + [row["resource_id"] for row in view["resources"]], + ) + self.assertEqual(["rapp-postgres"], view["consumers_potential"]) + self.assertEqual([], view["consumers_actual"]) + if __name__ == "__main__": unittest.main() From f9af7518f52cc704cec22ba1b204418ca52017dc Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 16:18:16 +0200 Subject: [PATCH 2/5] feat: WP-0002 T03 selection decision, wait on purchase Recommend Scaleway Multi-AZ nl-ams. Inventory stays proposed with description, decision, reef: refs, secret: handle, and consumers. Human approval required before ordered. --- .../platform-audit-storage.proposed.json | 34 +++++++- ...CE-WP-0002-primary-selection-2026-08-14.md | 82 +++++++++++++++++++ schemas/resource-inventory.schema.json | 31 +++++++ tests/test_portfolio.py | 12 +++ tools/portfolio.py | 12 +++ ...WP-0002-procure-postgres-backup-storage.md | 13 ++- 6 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md diff --git a/data/resources/platform-audit-storage.proposed.json b/data/resources/platform-audit-storage.proposed.json index 49dba12..72e59d6 100644 --- a/data/resources/platform-audit-storage.proposed.json +++ b/data/resources/platform-audit-storage.proposed.json @@ -5,6 +5,33 @@ "financial_entity_id": "entity:railiance", "procuring_entity_id": "entity:railiance", "entity_gap": null, + "description": "Off-host S3-compatible object store for rapp-postgres WAL archive and physical base backups. Procured by Railiance, operated as a Scaleway-delegated substrate on reef-storage, not on reef-railiance.", + "decision": { + "status": "recommended", + "chosen": "Scaleway Standard Multi-AZ nl-ams; independent secondary copy on Host Europe Backup Storage or governed Nextcloud (T06)", + "rejected": [ + "Host Europe Cloud Storage as primary (S3 not confirmed orderable)", + "Hetzner Object Storage as primary (no default at-rest encryption)", + "Self-managed Garage as primary (labor and capacity lose at this workload)" + ], + "approved_by": null, + "approved_on": null, + "ref": "docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md" + }, + "operational_refs": [ + "reef:storage/declarations/reef.yaml", + "reef:storage/substrate/object-stores/platform-audit-storage.yaml", + "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" + ], + "credential_handles": [ + "secret:railiance-platform/platform-pg-backup-s3" + ], + "consumers": { + "potential": ["rapp-postgres"], + "actual": [] + }, "resource_class": "storage", "status": "proposed", "management_model": "provider_managed", @@ -23,7 +50,7 @@ "location": { "region": "nl-ams", "country": "NL", - "failure_domains": ["provider:scaleway", "region:nl-ams"], + "failure_domains": ["provider:scaleway", "region:nl-ams", "reef:storage"], "residency": "European Union" }, "capacity": [ @@ -44,7 +71,7 @@ "cost": { "currency": "EUR", "tax_status": "excluded", - "billing_model": "usage-based storage and egress; no commitment", + "billing_model": "usage-based storage and egress; no commitment; Railiance self-use at delivered cost", "commitment_ref": null, "price_evidence": "data/providers/object-storage.json#scaleway-standard-multi-az" }, @@ -65,6 +92,7 @@ "evidence": [ {"kind": "provider", "ref": "https://www.scaleway.com/en/pricing/storage/", "observed_at": "2026-08-10", "authority": "Scaleway"}, {"kind": "provider", "ref": "https://www.scaleway.com/en/object-storage/", "observed_at": "2026-08-10", "authority": "Scaleway"}, - {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-provider-due-diligence-2026-08-10.md", "observed_at": "2026-08-10", "authority": "resource-control"} + {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-provider-due-diligence-2026-08-10.md", "observed_at": "2026-08-10", "authority": "resource-control"}, + {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md", "observed_at": "2026-08-14", "authority": "resource-control"} ] } diff --git a/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md new file mode 100644 index 0000000..eb7efee --- /dev/null +++ b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md @@ -0,0 +1,82 @@ +# RESOURCE-WP-0002 T03 — primary object-store selection + +Date: 2026-08-14 +Status: **recommended — awaiting human purchase approval** +Resource: `resource:platform:audit-storage` +Procuring / consuming entity: `entity:railiance` (self-use, no 20 % markup) +Operating reef: `reef-storage` (not `reef-railiance`) + +This is the decision record T03 asked for. It is not a purchase. It does +not create a Scaleway account, bucket, or key. + +## Recommendation + +| Role | Choice | Why | +| --- | --- | --- | +| **Primary** | Scaleway Object Storage, Standard Multi-AZ, region `nl-ams` (NL, EU) | Only A/B/C candidate that is orderable, S3/SigV4 documented, Multi-AZ, published durability, **managed encryption at rest**, and an independent failure domain from Host Europe `railiance01`. Lowest comparable 320 GB running cost among managed options that meet acceptance. | +| **Independent secondary copy** | Host Europe Backup Storage (SFTP/SCP) or the existing governed Nextcloud lane (T06) | Not S3; not a Barman primary. Keeps a second copy off Scaleway and off the same API credential. | + +Do **not** put the primary bucket on `reef-railiance`. S3 is a +provider-delegated capability; `reef-storage` is the substrate. + +## Ranking (A / B / C) + +Evidence: demand/cost model 2026-08-10, expanded comparison 2026-08-10, +due diligence 2026-08-10. Labor €60/h. Tax excluded. Running totals at +the normalized 320 GB + 5 GB restore-drill point. + +| Criterion | A Host Europe Cloud Storage | B Scaleway Multi-AZ `nl-ams` | C Hetzner Object Storage | +| --- | --- | --- | --- | +| Total cost (320 GB) | unknown (no current S3 quote) | **€65.14**/mo (€5.14 infra + €60 labor) | €96.49/mo (€6.49 min + €90 labor) | +| Compatibility (CNPG/Barman S3) | unknown / not orderable on evidence | documented S3 + SigV4; live preflight still required | documented S3; live preflight still required | +| Resilience | same provider as compute | **different provider**; Multi-AZ; 99.999999999% durability claim | different provider; no quantified storage SLA | +| Sovereignty | DE if it existed | NL / EU | DE / EU | +| Operational effort | unknown | 1 h/mo planned; no rail to run | 1.5 h/mo; SSE-C custody if we accept no default at-rest encryption | +| Exit cost | unknown | egress €0.01/GB after 75 GB free + 4 h labor | inside 1 TB included until quota exceeded | +| Blocking gap | current S3 **not confirmed orderable** | live Barman preflight; contract/tax on the paying account | **no default at-rest encryption** (SSE-C only) | + +Self-managed Garage on 2–3 VMs is €240–€336/mo at 320 GB and fails closed +before month-12 base volume. It is not a primary candidate at this +workload. + +## What we are buying (if approved) + +- Product: Scaleway Standard Multi-AZ Object Storage +- Region: `nl-ams` +- Commitment: **none** (usage-based) +- Payer: Railiance (`entity:railiance`); transfer price = delivered cost +- Public access: disabled +- Identity: narrowest key, bucket/prefix only +- Versioning: on +- Lifecycle: 30-day recovery window (match demand) +- Cost alert: on the Scaleway project +- Owner in inventory: `resource-control` +- Attribute home: `reef-storage/substrate/object-stores/platform-audit-storage.yaml` +- Credential home (after T04): `secret:railiance-platform/platform-pg-backup-s3` +- Consumer potential: `rapp-postgres` +- Consumer actual: none until WAL flows + +## What human financial authority must approve + +1. Create or reuse a Scaleway project paid as Railiance (or GmbH Hauptkonto + until the Railiance account exists). +2. Accept Scaleway’s contract/tax treatment for that account. +3. Accept that Host Europe S3 stays out of the race until written + orderability exists. +4. Accept Hetzner only as a price comparator unless SSE-C custody is + explicitly chosen later. +5. Spend: expected **~€3–€10/mo infrastructure** at current size, plus + ~1 h operator labor; not a committed term. + +After **yes**: create private bucket, scoped key, versioning/lifecycle, +cost alert; fill `reef-storage` attributes (endpoint, bucket, prefix, +project ref); flip inventory `proposed → ordered`; then T04/T05. + +After **no**: write the rejection on this record; do not invent another +primary without a new decision. + +## Authority + +Recommended by: resource-control (this file) +Approved by: _vacant — human financial authority_ +Approved on: _null_ diff --git a/schemas/resource-inventory.schema.json b/schemas/resource-inventory.schema.json index 2f3f719..33b16cb 100644 --- a/schemas/resource-inventory.schema.json +++ b/schemas/resource-inventory.schema.json @@ -12,6 +12,37 @@ "financial_entity_id": {"type": ["string", "null"], "pattern": "^entity:[a-z0-9]+$"}, "procuring_entity_id": {"type": ["string", "null"], "pattern": "^entity:[a-z0-9]+$"}, "entity_gap": {"type": ["string", "null"]}, + "description": {"type": "string", "minLength": 1}, + "decision": { + "type": "object", + "additionalProperties": false, + "required": ["status", "ref"], + "properties": { + "status": {"enum": ["recommended", "approved", "rejected"]}, + "chosen": {"type": ["string", "null"]}, + "rejected": {"type": "array", "items": {"type": "string"}}, + "approved_by": {"type": ["string", "null"]}, + "approved_on": {"type": ["string", "null"], "format": "date"}, + "ref": {"type": "string", "minLength": 1} + } + }, + "operational_refs": { + "type": "array", + "items": {"type": "string", "pattern": "^reef:"} + }, + "credential_handles": { + "type": "array", + "items": {"type": "string", "pattern": "^secret:"} + }, + "consumers": { + "type": "object", + "additionalProperties": false, + "required": ["potential", "actual"], + "properties": { + "potential": {"type": "array", "items": {"type": "string"}}, + "actual": {"type": "array", "items": {"type": "string"}} + } + }, "resource_class": {"enum": ["compute_instance", "storage", "network", "kubernetes_capacity", "database", "managed_service", "self_managed_service", "shared_platform_service", "license"]}, "status": {"enum": ["proposed", "ordered", "commissioning", "active", "suspended", "retiring", "retired", "rejected"]}, "management_model": {"enum": ["provider_managed", "self_managed", "shared_capacity"]}, diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index 475d951..756dd2f 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -34,6 +34,18 @@ class PortfolioTest(unittest.TestCase): with self.assertRaisesRegex(ValueError, "shared resources"): validate_record(record) + def test_ordered_resource_requires_approved_decision(self): + record = deepcopy(next( + r for _, r in self.records() if r["id"] == "resource:platform:audit-storage" + )) + record["status"] = "ordered" + with self.assertRaisesRegex(ValueError, "approved decision"): + validate_record(record) + record["decision"]["status"] = "approved" + record["decision"]["approved_by"] = "human" + record["decision"]["approved_on"] = "2026-08-14" + validate_record(record) + def test_unknown_commission_date_is_preserved(self): record = deepcopy(next(r for _, r in self.records() if r["status"] == "active")) record["lifecycle"]["commissioned_on"] = None diff --git a/tools/portfolio.py b/tools/portfolio.py index 0a71f84..e9e39bf 100644 --- a/tools/portfolio.py +++ b/tools/portfolio.py @@ -51,6 +51,18 @@ def validate_record(record: dict) -> None: association_ok(record) + decision = record.get("decision") + if record.get("status") in {"ordered", "commissioning"} and ( + not decision or decision.get("status") != "approved" + ): + raise ValueError("ordered or commissioning resources require an approved decision") + for ref in record.get("operational_refs") or []: + if not str(ref).startswith("reef:"): + raise ValueError(f"operational_refs must be reef: references: {ref}") + for ref in record.get("credential_handles") or []: + if not str(ref).startswith("secret:"): + raise ValueError(f"credential_handles must be secret: references: {ref}") + allocation = record["ownership"]["allocation"] if allocation["mode"] == "unattributed": if allocation["cost_attribution_key"] is not None: diff --git a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md index de50eab..72d6594 100644 --- a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md +++ b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md @@ -8,7 +8,7 @@ status: active owner: codex topic_slug: railiance created: "2026-08-10" -updated: "2026-08-10" +updated: "2026-08-14" state_hub_workstream_id: "921496a3-280b-4dc8-a3c0-b4ec314142f5" --- @@ -190,7 +190,7 @@ Barman preflight, contract review, and human approval. ```task id: RESOURCE-WP-0002-T03 -status: wait +status: progress priority: high state_hub_task_id: "e4184350-dab2-4a0b-bee5-1a641e8a2df3" ``` @@ -207,6 +207,15 @@ Done when the decision is approved and the purchased resource has a non-secret inventory record with provider resource ID, region, service class, contract, renewal/cancellation dates, capacity model, owner, and cost-attribution key. +Progress 2026-08-14: decision record written — +`docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md`. +Primary: Scaleway Multi-AZ `nl-ams`. Secondary copy: Host Europe Backup +Storage or Nextcloud (T06). Inventory stays `proposed` with five-facet +refs to `reef-storage`. Reef seeded (identity, topology, consumers, +planned attribute file). **Blocked on human approval to create the +Scaleway project/bucket.** After yes: fill reef attributes, set +`decision.status: approved` and inventory `ordered`. + ## T04 — Establish credential custody and hand off to rapp-postgres ```task From 2ebc000f04b5503cd3af31e3785f19c6e8a342c2 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 16:58:44 +0200 Subject: [PATCH 3/5] docs: approve WP-0002 T03; wait on OpenBao bootstrap key Human approved the Scaleway buy. Decision and inventory record approved_on 2026-08-14. Bucket not created until the bootstrap key is in OpenBao. --- .../resources/platform-audit-storage.proposed.json | 6 +++--- ...ESOURCE-WP-0002-primary-selection-2026-08-14.md | 14 +++++++++----- ...URCE-WP-0002-procure-postgres-backup-storage.md | 8 +++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/data/resources/platform-audit-storage.proposed.json b/data/resources/platform-audit-storage.proposed.json index 72e59d6..464614c 100644 --- a/data/resources/platform-audit-storage.proposed.json +++ b/data/resources/platform-audit-storage.proposed.json @@ -7,15 +7,15 @@ "entity_gap": null, "description": "Off-host S3-compatible object store for rapp-postgres WAL archive and physical base backups. Procured by Railiance, operated as a Scaleway-delegated substrate on reef-storage, not on reef-railiance.", "decision": { - "status": "recommended", + "status": "approved", "chosen": "Scaleway Standard Multi-AZ nl-ams; independent secondary copy on Host Europe Backup Storage or governed Nextcloud (T06)", "rejected": [ "Host Europe Cloud Storage as primary (S3 not confirmed orderable)", "Hetzner Object Storage as primary (no default at-rest encryption)", "Self-managed Garage as primary (labor and capacity lose at this workload)" ], - "approved_by": null, - "approved_on": null, + "approved_by": "human-financial-authority", + "approved_on": "2026-08-14", "ref": "docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md" }, "operational_refs": [ diff --git a/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md index eb7efee..903d0f2 100644 --- a/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md +++ b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md @@ -1,13 +1,17 @@ # RESOURCE-WP-0002 T03 — primary object-store selection Date: 2026-08-14 -Status: **recommended — awaiting human purchase approval** +Status: **approved — waiting on Scaleway bootstrap key in OpenBao** Resource: `resource:platform:audit-storage` Procuring / consuming entity: `entity:railiance` (self-use, no 20 % markup) Operating reef: `reef-storage` (not `reef-railiance`) -This is the decision record T03 asked for. It is not a purchase. It does -not create a Scaleway account, bucket, or key. +Human financial authority approved the purchase in session 2026-08-14 +(“lets do it”, then “key into OpenBao first”). This file is no longer a +recommendation-only draft. The bucket is **not** created until the +bootstrap API key is in OpenBao +(`platform/workloads/railiance/scaleway/bootstrap`). See +`reef-storage/docs/put-scaleway-bootstrap.md`. ## Recommendation @@ -78,5 +82,5 @@ primary without a new decision. ## Authority Recommended by: resource-control (this file) -Approved by: _vacant — human financial authority_ -Approved on: _null_ +Approved by: human financial authority (Bernd Worsch, chat 2026-08-14) +Approved on: 2026-08-14 diff --git a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md index 72d6594..d71d50a 100644 --- a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md +++ b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md @@ -212,9 +212,11 @@ Progress 2026-08-14: decision record written — Primary: Scaleway Multi-AZ `nl-ams`. Secondary copy: Host Europe Backup Storage or Nextcloud (T06). Inventory stays `proposed` with five-facet refs to `reef-storage`. Reef seeded (identity, topology, consumers, -planned attribute file). **Blocked on human approval to create the -Scaleway project/bucket.** After yes: fill reef attributes, set -`decision.status: approved` and inventory `ordered`. +planned attribute file). **Purchase approved 2026-08-14.** Blocked on founder putting the +Scaleway bootstrap API key in OpenBao +(`platform/workloads/railiance/scaleway/bootstrap`, +`reef-storage/docs/put-scaleway-bootstrap.md`). Do not paste the +secret in chat. Then run `reef-storage/tools/create-platform-audit-bucket.sh`. ## T04 — Establish credential custody and hand off to rapp-postgres From 1a9daf93ef134826cc4a1fcd324e152de4d9b86e Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 17:48:04 +0200 Subject: [PATCH 4/5] note: WP-0002 T03 blocked on Scaleway Object Storage IAM --- ...SOURCE-WP-0002-procure-postgres-backup-storage.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md index d71d50a..8899806 100644 --- a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md +++ b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md @@ -212,11 +212,13 @@ Progress 2026-08-14: decision record written — Primary: Scaleway Multi-AZ `nl-ams`. Secondary copy: Host Europe Backup Storage or Nextcloud (T06). Inventory stays `proposed` with five-facet refs to `reef-storage`. Reef seeded (identity, topology, consumers, -planned attribute file). **Purchase approved 2026-08-14.** Blocked on founder putting the -Scaleway bootstrap API key in OpenBao -(`platform/workloads/railiance/scaleway/bootstrap`, -`reef-storage/docs/put-scaleway-bootstrap.md`). Do not paste the -secret in chat. Then run `reef-storage/tools/create-platform-audit-bucket.sh`. +planned attribute file). **Purchase approved 2026-08-14.** Bootstrap key is in OpenBao (v2, +not placeholders). Bucket create returned **Access Denied** on S3 +while Instances list succeeded — the key is valid but Object Storage +is not permitted or not enabled on the project. Founder: in the +Scaleway console open Object Storage once for that project and attach +`ObjectStorageFullAccess` (or equivalent) to the API key, then say +go again. ## T04 — Establish credential custody and hand off to rapp-postgres From a6ac303d98c98226d2287e0b1b878f65635713d6 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 18:28:27 +0200 Subject: [PATCH 5/5] feat: order Scaleway backup bucket for WP-0002 T03 Policy now allows Object Storage. Inventory moves proposed -> ordered with provider_resource_id and reef-storage attribute refs. Cost alert remains a founder console action; scoped Barman key is T04. --- ...posed.json => platform-audit-storage.json} | 16 +++++++----- ...CE-WP-0002-primary-selection-2026-08-14.md | 17 ++++++++----- tests/test_portfolio.py | 1 + ...WP-0002-procure-postgres-backup-storage.md | 25 +++++++++++-------- 4 files changed, 36 insertions(+), 23 deletions(-) rename data/resources/{platform-audit-storage.proposed.json => platform-audit-storage.json} (85%) diff --git a/data/resources/platform-audit-storage.proposed.json b/data/resources/platform-audit-storage.json similarity index 85% rename from data/resources/platform-audit-storage.proposed.json rename to data/resources/platform-audit-storage.json index 464614c..2b3c8b6 100644 --- a/data/resources/platform-audit-storage.proposed.json +++ b/data/resources/platform-audit-storage.json @@ -23,7 +23,10 @@ "reef:storage/substrate/object-stores/platform-audit-storage.yaml", "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:storage/substrate/object-stores/platform-audit-storage.yaml#region", + "reef:storage/substrate/object-stores/platform-audit-storage.yaml#prefix", + "reef:storage/substrate/object-stores/platform-audit-storage.yaml#lifecycle", + "reef:storage/substrate/object-stores/platform-audit-storage.yaml#provider_project_ref" ], "credential_handles": [ "secret:railiance-platform/platform-pg-backup-s3" @@ -33,13 +36,13 @@ "actual": [] }, "resource_class": "storage", - "status": "proposed", + "status": "ordered", "management_model": "provider_managed", "provider": { "name": "Scaleway", - "account_ref": null, + "account_ref": "reef:storage/substrate/object-stores/platform-audit-storage.yaml#provider_project_ref", "product_ref": "scaleway-standard-multi-az", - "provider_resource_id": null + "provider_resource_id": "railiance-platform-pg-backup" }, "service": { "name": "platform audit storage", @@ -77,7 +80,7 @@ }, "lifecycle": { "proposed_on": "2026-08-10", - "ordered_on": null, + "ordered_on": "2026-08-14", "commissioned_on": null, "renews_on": null, "cancel_by": null, @@ -93,6 +96,7 @@ {"kind": "provider", "ref": "https://www.scaleway.com/en/pricing/storage/", "observed_at": "2026-08-10", "authority": "Scaleway"}, {"kind": "provider", "ref": "https://www.scaleway.com/en/object-storage/", "observed_at": "2026-08-10", "authority": "Scaleway"}, {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-provider-due-diligence-2026-08-10.md", "observed_at": "2026-08-10", "authority": "resource-control"}, - {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md", "observed_at": "2026-08-14", "authority": "resource-control"} + {"kind": "decision", "ref": "docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md", "observed_at": "2026-08-14", "authority": "resource-control"}, + {"kind": "provider", "ref": "reef:storage/substrate/object-stores/platform-audit-storage.yaml", "observed_at": "2026-08-14", "authority": "reef-storage"} ] } diff --git a/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md index 903d0f2..026da81 100644 --- a/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md +++ b/docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md @@ -1,17 +1,22 @@ # RESOURCE-WP-0002 T03 — primary object-store selection Date: 2026-08-14 -Status: **approved — waiting on Scaleway bootstrap key in OpenBao** +Status: **approved and purchased — cost alert still human** Resource: `resource:platform:audit-storage` Procuring / consuming entity: `entity:railiance` (self-use, no 20 % markup) Operating reef: `reef-storage` (not `reef-railiance`) Human financial authority approved the purchase in session 2026-08-14 -(“lets do it”, then “key into OpenBao first”). This file is no longer a -recommendation-only draft. The bucket is **not** created until the -bootstrap API key is in OpenBao -(`platform/workloads/railiance/scaleway/bootstrap`). See -`reef-storage/docs/put-scaleway-bootstrap.md`. +(“lets do it”, then “key into OpenBao first”). Bootstrap key is in OpenBao +(`platform/workloads/railiance/scaleway/bootstrap`, KV v2). After the +Object Storage policy attached, list/get succeeded. Private bucket +`railiance-platform-pg-backup` exists in `nl-ams` (created +2026-08-14T16:21:56Z), versioning on, 30-day lifecycle applied, ACL +owner-only. Live attributes are in +`reef-storage/substrate/object-stores/platform-audit-storage.yaml`. +The bootstrap key cannot `write billing_budgets`; founder still sets a +€20 monthly project budget in the Scaleway console. Scoped Barman key is +T04. ## Recommendation diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index 756dd2f..a4c0ae4 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -39,6 +39,7 @@ class PortfolioTest(unittest.TestCase): r for _, r in self.records() if r["id"] == "resource:platform:audit-storage" )) record["status"] = "ordered" + record["decision"]["status"] = "draft" with self.assertRaisesRegex(ValueError, "approved decision"): validate_record(record) record["decision"]["status"] = "approved" diff --git a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md index 8899806..f46ed55 100644 --- a/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md +++ b/workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md @@ -190,7 +190,7 @@ Barman preflight, contract review, and human approval. ```task id: RESOURCE-WP-0002-T03 -status: progress +status: done priority: high state_hub_task_id: "e4184350-dab2-4a0b-bee5-1a641e8a2df3" ``` @@ -210,21 +210,24 @@ renewal/cancellation dates, capacity model, owner, and cost-attribution key. Progress 2026-08-14: decision record written — `docs/evidence/RESOURCE-WP-0002-primary-selection-2026-08-14.md`. Primary: Scaleway Multi-AZ `nl-ams`. Secondary copy: Host Europe Backup -Storage or Nextcloud (T06). Inventory stays `proposed` with five-facet -refs to `reef-storage`. Reef seeded (identity, topology, consumers, -planned attribute file). **Purchase approved 2026-08-14.** Bootstrap key is in OpenBao (v2, -not placeholders). Bucket create returned **Access Denied** on S3 -while Instances list succeeded — the key is valid but Object Storage -is not permitted or not enabled on the project. Founder: in the -Scaleway console open Object Storage once for that project and attach -`ObjectStorageFullAccess` (or equivalent) to the API key, then say -go again. +Storage or Nextcloud (T06). **Purchase approved 2026-08-14.** After the +Object Storage policy attached, `scw object bucket list/get` succeeded. +Private bucket `railiance-platform-pg-backup` exists in `nl-ams` +(created 2026-08-14T16:21:56Z), versioning on, 30-day current and +noncurrent lifecycle applied, ACL owner-only. Inventory is +`data/resources/platform-audit-storage.json` (`status: ordered`, +`ordered_on: 2026-08-14`, `provider_resource_id` = bucket name). +Operating facts live on +`reef:storage/substrate/object-stores/platform-audit-storage.yaml`. +Residual: founder must create a €20 monthly Scaleway budget in the +console — this bootstrap key cannot `write billing_budgets`. Scoped +Barman key remains T04. ## T04 — Establish credential custody and hand off to rapp-postgres ```task id: RESOURCE-WP-0002-T04 -status: wait +status: todo priority: high state_hub_task_id: "a2dc370a-b5e7-44b1-b46a-f3b84815b14a" ```