Compare commits

...
Sign in to create a new pull request.

39 commits

Author SHA1 Message Date
eaed77ee25 Record productive owner copy and move acceptance
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m18s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-11-01 00:48:13 +01:00
860875c1f0 Verify revision 21 and record accepted editor attachment visibility
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-11-01 00:41:55 +01:00
bcc2944cb7 Prepare verified editor attachment correction for attended release
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m17s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 22:16:35 +01:00
8b38afddf3 Show saved attachments in editor and after stale edits
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 21:50:45 +01:00
e2dfb2f6ae Record remaining productive acceptance after verified release
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m37s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 21:30:29 +01:00
479a15024d Verify revision 20 content release and prepare productive acceptance
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 21:29:27 +01:00
a20e322b0e Accept omission of the four obsolete legacy scrap cases
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m15s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 20:57:27 +01:00
821dbdb80b Isolate the four remaining scrap fidelity decisions
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 20:51:57 +01:00
37e3bfc2c7 Recover original attachments through the confirmed Bubble modal
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m29s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 20:37:49 +01:00
43b38429f4 Index the attachment acceptance task in State Hub
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 3m19s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:43:30 +01:00
da09364b18 Record tested attachment release and request exact founder execution
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:42:28 +01:00
d9da608e65 Add private author-managed attachments with retained revisions
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:38:10 +01:00
075633e4b5 Verify backup route and inactive installation prerequisites for content acceptance
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m15s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 18:36:07 +01:00
a9526e7fb3 Preserve original ordered-list numbering during sanitization
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 18:21:13 +01:00
80fe387dcc Complete matching uncertain import checkpoints with retained evidence
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 18:07:34 +01:00
b800daedbc Record verified historical backup custody repair and operator handoff
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m44s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 17:43:38 +01:00
e75d3b0f6a Bind import attempts to exclusive space identities and add inspection
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m14s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 17:31:38 +01:00
0cc45be42c Track sender executor completion and credential owner dependency
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 2m31s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 17:19:15 +01:00
a45ededf45 Record native sender adapter evidence and remaining release gates
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 8m2s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 17:03:16 +01:00
f1d03fbfac Track sender recovery protocol progress and native execution remainder
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 1m59s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 16:41:31 +01:00
ea7662eb97 Link sender transition owner and native ledger evidence
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m0s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 16:23:41 +01:00
dd9a5bfbfe Add durable corpus import checkpoints without replaying completed spaces
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m35s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 16:10:54 +01:00
f34d46d9f6 Define concrete client offboarding scope and retained data boundaries
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 2m27s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 15:56:54 +01:00
8067a51743 Record native telemetry limits and current service conformance
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m28s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 15:45:49 +01:00
0ee31b596e Capture bounded source resource evidence and expose navigation gap
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m36s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 15:37:08 +01:00
6e07f572a8 Verify captured body links and retain unresolved scrap relations
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m58s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 15:13:45 +01:00
52168c3bd4 Inventory retained source access fields without inferring grants
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 15:07:15 +01:00
5c8f78ce43 Record Visual capture task projection and completion time
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m13s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:55:09 +01:00
0c714d3c56 Preserve captured Bubble Visuals in private review trees
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:54:06 +01:00
000ca43f0c Record goal task projection readback and updated index
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m29s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:42:05 +01:00
5fefc0108d Resolve service forge and identity decision provenance
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:40:57 +01:00
d5adc4b998 Review reset authority transition and same-process role refresh
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m14s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:26:17 +01:00
776dfc20a0 Verify attended reset-role receipt for accepted personal account
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m59s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 14:00:36 +01:00
028f813434 Bind reset observation to accepted account and prepare operator steps
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 2m1s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:54:50 +01:00
f763379882 Verify native reset authority across account classes
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m5s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:40:10 +01:00
338340222d Clarify current reset dependency and update work-record index
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m0s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:32:26 +01:00
961f310a8e Assess password reset owner return after directory bind split
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:29:40 +01:00
a786a8050d Record content recovery release preparation and backup ordering
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 13:25:36 +01:00
4e9a1c62ee Publish verified content recovery candidate evidence
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m31s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:59:46 +01:00
151 changed files with 10605 additions and 100 deletions

View file

@ -92,31 +92,35 @@
| task | CSOC-WP-0012-T04 | progress | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T05 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T06 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T07 | wait | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T08 | wait | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T07 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T08 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T09 | todo | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T10 | todo | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T11 | todo | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T12 | todo | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T11 | progress | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T12 | progress | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T13 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T14 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0012-T15 | done | — | workplans/CSOC-WP-0012-durable-recovery-service-readiness.md |
| task | CSOC-WP-0013-T01 | done | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T02 | progress | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T03 | todo | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T03 | progress | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T04 | done | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T05 | todo | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T05 | progress | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0013-T06 | progress | — | workplans/CSOC-WP-0013-sharing-content-hardening.md |
| task | CSOC-WP-0014-T01 | progress | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T02 | todo | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T03 | wait | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T04 | done | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T05 | done | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T06 | progress | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T07 | todo | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T07 | progress | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T08 | done | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0015-T01 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T02 | progress | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T03 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T04 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T05 | todo | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T06 | todo | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T06 | progress | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0016-T01 | done | — | workplans/CSOC-WP-0016-mfa-setup-link.md |
| task | CSOC-WP-0017-T01 | done | — | workplans/CSOC-WP-0017-wave2-resource-right-sizing.md |
| intake | CSOC-IN-0001 | closed | blue | docs/intakes/csoc-residuals.md |

View file

@ -83,6 +83,13 @@ def open_local_image(space_slug: str, path: str):
def local_asset_url(space_slug: str, reference: str) -> str:
"""Map space-root or page-relative assets to the authenticated app route."""
path = local_asset_path(reference)
if not path or PurePosixPath(path).suffix.lower() not in _TYPES:
return ""
return reverse("spaces:asset", kwargs={"slug": space_slug, "asset_path": path})
def local_asset_path(reference: str) -> str:
try:
parsed = urlsplit(reference)
except ValueError:
@ -98,11 +105,16 @@ def local_asset_url(space_slug: str, reference: str) -> str:
return ""
try:
asset_parts(path)
if PurePosixPath(path).suffix.lower() not in _TYPES:
return ""
except ValueError:
return ""
return reverse("spaces:asset", kwargs={"slug": space_slug, "asset_path": path})
return path
def local_download_url(space_slug: str, reference: str) -> str:
path = local_asset_path(reference)
if not path:
return ""
return reverse("spaces:download", kwargs={"slug": space_slug, "asset_path": path})
class _AssetLinks(HTMLParser):
@ -115,7 +127,9 @@ class _AssetLinks(HTMLParser):
result = []
for name, value in attrs:
if (tag, name) in {("img", "src"), ("a", "href")} and value:
value = local_asset_url(self.space_slug, value) or value
value = (local_asset_url(self.space_slug, value)
or (local_download_url(self.space_slug, value) if tag == "a" else "")
or value)
result.append(name if value is None else f'{name}="{escape(value, quote=True)}"')
self.output.append("<" + tag + (" " + " ".join(result) if result else "") + ">")

View file

@ -0,0 +1,196 @@
"""Page attachments: immutable files, revisioned associations and bounded uploads."""
import hashlib
import json
import os
import re
import uuid
from html.parser import HTMLParser
from pathlib import Path
from django.utils.text import slugify
from . import pageops
from .assets import (
asset_parts,
local_asset_path,
local_asset_url,
local_download_url,
open_local_asset,
)
KEY = "coulomb_attachments"
MAX_UPLOAD_BYTES = 16 * 1024 * 1024
MAX_ATTACHMENTS = 100
def page_files(space, doc):
"""List page associations plus existing body/Visual references, without I/O."""
from .content import render_markdown
files = {local_asset_path(e["path"]): {**e, "managed": True} for e in entries(doc.meta)}
def add_reference(value):
path = local_asset_path(value)
if path and path not in files:
files[path] = {"path": "assets/" + path, "name": Path(path).name, "managed": False}
class Links(HTMLParser):
def handle_starttag(self, tag, attrs):
for key, value in attrs:
if (tag, key) in {("a", "href"), ("img", "src")} and value:
add_reference(value)
parser = Links()
parser.feed(render_markdown(doc.body))
parser.close()
add_reference(doc.visual)
return [{**entry, "download_url": local_download_url(space, entry["path"]),
"open_url": local_asset_url(space, entry["path"])} for entry in files.values()]
def entries(meta):
raw = meta.get(KEY, "[]")
try:
result = json.loads(raw)
if not isinstance(result, list) or len(result) > MAX_ATTACHMENTS:
raise ValueError
paths = set()
for entry in result:
if (not isinstance(entry, dict) or set(entry) != {"name", "path"}
or not isinstance(entry["name"], str) or not entry["name"]
or len(entry["name"]) > 200 or not isinstance(entry["path"], str)
or not entry["path"].startswith("assets/")):
raise ValueError
asset_parts(entry["path"][7:])
if entry["path"] in paths:
raise ValueError
paths.add(entry["path"])
return result
except (ValueError, TypeError, KeyError) as exc:
raise pageops.PageOpsError("The attachment list needs operator review") from exc
def _metadata(space, doc, files):
meta = dict(title=doc.title, abstractor=doc.abstractor, visual=doc.visual,
space=space)
meta.update({k: v for k, v in doc.meta.items() if k not in meta})
meta[KEY] = json.dumps(files, ensure_ascii=True)
return meta
def _current(space, page, revision):
doc = pageops.read_page(space, page)
if not doc.exists:
raise pageops.PageOpsError("Page not found")
if not revision or doc.revision != revision:
raise pageops.PageOpsError("This page changed. Reopen it before changing attachments")
return doc
def _save(space, doc, meta):
return pageops.write_page(
space, doc.slug, title=doc.title, abstractor=doc.abstractor,
visual=doc.visual, body=doc.body, extra_meta=meta, expected_revision=doc.revision,
)
@pageops.content_writer
def add(space, page, upload, *, revision):
doc = _current(space, page, revision)
files = entries(doc.meta)
if len(files) >= MAX_ATTACHMENTS:
raise pageops.PageOpsError("This page already has 100 attachments")
if upload is None or upload.size > MAX_UPLOAD_BYTES:
raise pageops.PageOpsError("Choose a file no larger than 16 MiB")
raw = upload.read(MAX_UPLOAD_BYTES + 1)
if not raw or len(raw) > MAX_UPLOAD_BYTES:
raise pageops.PageOpsError("Choose a non-empty file no larger than 16 MiB")
label = Path(upload.name.replace("\\", "/")).name
label = "".join(c for c in label if c.isprintable())[:200] or "attachment"
suffix = Path(label).suffix.lower()
if not re.fullmatch(r"\.[a-z0-9]{1,12}", suffix):
suffix = ""
stem = slugify(Path(label).stem)[:80] or "attachment"
identity = uuid.uuid4().hex
name = f"upload-{identity}-{stem}{suffix}"
path = pageops.assets_dir(space) / name
# Exclusive creation and the shared writer lock preserve every older asset.
if path.exists() or path.is_symlink():
raise pageops.PageOpsError("Attachment address already exists")
meta = _metadata(space, doc, [*files, {"name": label, "path": "assets/" + name}])
record = {
"id": identity, "version": 1, "op": "attachment_upload", "phase": "started",
"source_space": space, "dest_space": space,
"source_page": doc.slug, "dest_page": doc.slug,
"source_revision": doc.revision,
"dest_revision": hashlib.sha256(
pageops.dump_frontmatter(meta, doc.body).encode()).hexdigest(),
"assets": [name], "asset_sha256": hashlib.sha256(raw).hexdigest(),
}
pageops._write_record(record)
try:
with open_local_asset(space, name, create=True) as stream:
stream.write(raw)
stream.flush()
os.fsync(stream.fileno())
pageops._fsync_directory(path.parent)
result = _save(space, doc, meta)
if result.revision != record["dest_revision"]:
raise pageops.PageOpsError("Attachment publication needs operator review")
pageops._clear_record(record)
return result
except BaseException:
pageops._recover_record(record)
raise
@pageops.content_writer
def remove(space, page, reference, *, revision):
doc = _current(space, page, revision)
files = entries(doc.meta)
remaining = [entry for entry in files if entry["path"] != reference]
if len(remaining) == len(files):
raise pageops.PageOpsError("Attachment not found on this page")
# File bytes remain available to retained revisions and other page references.
return _save(space, doc, _metadata(space, doc, remaining))
def recover(record):
"""Keep revision-referenced complete bytes; discard only this failed upload."""
try:
identity = record["id"]
if not re.fullmatch(r"[a-f0-9]{32}", identity):
return "held"
space, page = record["source_space"], record["source_page"]
if (record.get("version") != 1 or record.get("phase") != "started"
or record["dest_space"] != space or record["dest_page"] != page
or len(record["assets"]) != 1):
return "held"
name = record["assets"][0]
if not name.startswith(f"upload-{identity}-") or len(asset_parts(name)) != 1:
return "held"
for field in ("source_revision", "dest_revision", "asset_sha256"):
if not re.fullmatch(r"[a-f0-9]{64}", record[field]):
return "held"
current = pageops.page_file(space, page)
if pageops.assets_dir(space).is_symlink():
return "held"
revision = pageops.content_root() / ".revisions" / space / page / (
record["dest_revision"] + ".md")
if any(p.is_symlink() for p in (revision, *revision.parents)):
return "held"
if revision.exists():
if pageops._file_revision(revision) != record["dest_revision"]:
return "held"
with open_local_asset(space, name) as stream:
if hashlib.file_digest(stream, "sha256").hexdigest() != record["asset_sha256"]:
return "held"
elif current.is_file() and pageops._file_revision(current) == record["source_revision"]:
pageops._discard_recorded_assets(space, [name])
else:
return "held"
pageops._clear_record(record)
return "cleared"
except (KeyError, TypeError, ValueError, OSError, pageops.PageOpsError):
return "held"

View file

@ -143,6 +143,18 @@ def prepare_bundle(spaces, articles, *, inventory_spaces=None):
detail["relations"]["context_custom_article"] = normalize_ref(parent) if parent else None
if article.get("hasattachments_boolean"):
detail["review_flags"].append("attachment_records_require_capture")
if "_list_custom_scrap" in article:
# Bubble may return a deferred LOOKUP string instead of the actual
# list. Preserve it privately; it is not a captured scrap record or
# an article ID that may be traversed as a chunk.
raw_scraps = article["_list_custom_scrap"]
detail["scrap_relation"] = {
"raw": raw_scraps,
"state": "observed_empty_list" if raw_scraps == [] else "unresolved",
"records_captured": False,
}
if raw_scraps != []:
detail["review_flags"].append("scrap_relation_records_require_capture")
article_details[identity] = detail
for entry in plan["spaces"]:

View file

@ -52,6 +52,7 @@ _ALLOWED_ATTRS = {
"img": ["src", "alt", "title"],
"a": ["href", "title", "rel"],
"code": ["class"],
"ol": ["start"],
"th": ["align"],
"td": ["align"],
}
@ -100,7 +101,8 @@ def render_markdown(text: str) -> str:
"""Render the supported Markdown subset.
Extensions are fenced code, tables, newline-to-break and sane lists.
Raw HTML, script, event handlers and non-http(s)/mailto URLs are removed.
Ordered-list starting numbers are preserved. Unsupported HTML, scripts,
event handlers and non-http(s)/mailto URLs are removed.
Relative asset URLs are kept so copied page images still resolve. This is
not acceptance of a private corpus or of arbitrary Bubble markup.
"""

View file

@ -0,0 +1,181 @@
"""Read-only classification of uncertain corpus imports; never repairs a store."""
from __future__ import annotations
import fcntl
import hashlib
import os
import uuid
from contextlib import contextmanager
from django.db import connection, transaction
from . import pageops
from .import_receipts import digest, private_path, tree_digest
from .import_tree import _metadata, _source_dates
from .models import Space, SpaceMembership
@contextmanager
def existing_lock(path):
private_path(path)
# Inspection never creates a lock, journal, content root or receipt directory.
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
try:
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
raise ValueError("Import or content writer is active; inspect after it stops") from exc
yield
finally:
os.close(fd)
def expected_metadata(tree, tenant, slug, grants):
title, description, visual = _metadata(tree, slug)
values = {
"tenant_id": tenant,
"slug": slug,
"title": title,
"description": description,
"visual": visual,
"is_active": True,
"content_root": "pages",
"default_branch": "main",
"forgejo_owner": "",
"forgejo_repo": "",
"created_by_id": next(m.pk for m, role in grants if role == SpaceMembership.Role.OWNER),
}
index = tree / "pages/index.md"
if index.is_file():
meta, _ = pageops.parse_frontmatter(index.read_text(encoding="utf-8"))
values.update(_source_dates(meta))
return values
def observe_unit(receipts, index, unit):
tree, tenant, slug, grants = receipts.plan[index]
rows = list(Space.objects.filter(slug=slug).values())
destination = pageops.space_dir(slug)
if destination.is_symlink() or any(p.is_symlink() for p in destination.parents):
raise ValueError("Destination must not contain symlinks")
files_hash = tree_digest(destination) if destination.is_dir() else None
grant_rows = sorted(
(str(member), role)
for member, role in SpaceMembership.objects.filter(space__slug=slug).values_list(
"member_id", "role"
)
)
result = {
"index": index,
"checkpoint_state": unit.get("state"),
"sql_rows": len(rows),
"published_tree_present": destination.exists(),
"files_sha256": files_hash,
"space_sha256": digest(rows),
"grants_sha256": digest(grant_rows),
}
state = unit.get("state")
if state == "completed":
result["classification"] = (
"completed_identity_present"
if len(rows) == 1
and str(rows[0]["id"]) == unit.get("space_id")
and rows[0]["tenant_id"] == tenant
else "completed_identity_changed"
)
return result
if state not in {"pending", "started"}:
raise ValueError("Invalid checkpoint state")
if state == "pending":
result["classification"] = (
"pending_destination_occupied" if rows or destination.exists() else "pending"
)
return result
try:
reserved = uuid.UUID(unit["space_id"])
except (KeyError, ValueError, TypeError, AttributeError):
result["classification"] = "started_identity_unbound"
return result
if not rows and not destination.exists():
result["classification"] = "no_sql_or_published_tree"
return result
if not rows:
result["classification"] = "files_without_sql"
return result
if len(rows) != 1 or rows[0]["id"] != reserved or rows[0]["tenant_id"] != tenant:
result["classification"] = "foreign_destination_identity"
return result
if not destination.exists():
result["classification"] = "sql_without_files"
return result
wanted = expected_metadata(tree, tenant, slug, grants)
metadata_match = all(rows[0][key] == value for key, value in wanted.items())
grants_match = grant_rows == sorted((str(m.pk), role) for m, role in grants)
# The importer copies root Markdown pages and the assets subtree. Extra
# source files, or a synthesized index, cannot be called byte-exact here.
files_match = files_hash == receipts.source_hashes[index]
result.update(
metadata_matches=metadata_match,
grants_match=grants_match,
source_files_match=files_match,
original_space_date_checked="created_at" in wanted,
)
result["classification"] = (
"reserved_destination_matches_source"
if metadata_match and grants_match and files_match
else "reserved_destination_differs"
)
return result
@contextmanager
def inspection_session(receipts):
if connection.in_atomic_block or not connection.get_autocommit():
raise ValueError("Inspection requires an independent read transaction")
private_path(receipts.path)
if not receipts.path.is_file():
raise ValueError("An existing checkpoint is required for inspection")
with (
existing_lock(receipts.directory / ".lock"),
existing_lock(pageops.content_root() / ".writer.lock"),
):
checkpoint = receipts.path.read_bytes()
data = receipts.read()
if any(not isinstance(unit, dict) for unit in data["spaces"]):
raise ValueError("Invalid space checkpoint")
with transaction.atomic():
if connection.vendor == "postgresql":
with connection.cursor() as cursor:
cursor.execute("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY")
first = [observe_unit(receipts, i, unit) for i, unit in enumerate(data["spaces"])]
second = [observe_unit(receipts, i, unit) for i, unit in enumerate(data["spaces"])]
if first != second or checkpoint != receipts.path.read_bytes():
raise ValueError("Destination or checkpoint changed during inspection")
if any(
tree_digest(tree) != checksum
for (tree, _, _, _), checksum in zip(
receipts.plan, receipts.source_hashes, strict=True
)
):
raise ValueError("Source changed during inspection")
staging = list((pageops.content_root() / "spaces").glob(".import-*"))
result = {
"schema": "coulomb.corpus-import-inspection.v1",
"binding": receipts.binding,
"checkpoint_sha256": hashlib.sha256(checkpoint).hexdigest(),
"spaces": first,
"unattributed_staging_entries": len(staging),
"read_only": True,
"external_writer_fence_verified": False,
"checkpoint_completed": False,
"retry_authorized": False,
"data_restored": False,
}
result["inspection_sha256"] = digest(result)
yield result, data
def inspect(receipts):
with inspection_session(receipts) as (result, _):
return result

View file

@ -0,0 +1,235 @@
"""Opt-in corpus checkpoints; no automatic repair of uncertain DB/file commits.
The caller still owns source freezing, external writer fencing, paired backups
and admission. A started receipt survives a crash and stops automatic retries.
Completed receipts skip their space, preserving any subsequent local edits.
"""
from __future__ import annotations
import fcntl
import hashlib
import json
import os
import re
import stat
import tempfile
import uuid
from contextlib import contextmanager
from pathlib import Path
from django.db import connection
from . import pageops
from .import_tree import import_space_tree
from .models import Space, SpaceMembership
SCHEMA = "coulomb.corpus-import-checkpoint.v1"
def digest(value):
return hashlib.sha256(json.dumps(value, sort_keys=True, default=str).encode()).hexdigest()
def tree_digest(tree):
files = []
for path in sorted(tree.rglob("*")):
mode = path.lstat().st_mode
if stat.S_ISDIR(mode):
continue
if not stat.S_ISREG(mode):
raise ValueError("Import tree must contain only real files and directories")
with path.open("rb") as stream:
checksum = hashlib.file_digest(stream, "sha256").hexdigest()
files.append([path.relative_to(tree).as_posix(), checksum])
return digest(files)
def private_path(path, *, directory=False):
if any(p.is_symlink() for p in (path, *path.parents)):
raise ValueError("Receipt path must not contain symlinks")
if path.exists():
info = path.stat()
expected = stat.S_ISDIR if directory else stat.S_ISREG
if (not expected(info.st_mode) or info.st_uid != os.getuid()
or info.st_mode & 0o077 or (not directory and info.st_nlink != 1)):
raise ValueError("Receipt path must be private and owned by this operator")
def sync_directory(path):
fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
os.fsync(fd)
finally:
os.close(fd)
def sync_tree(tree):
# Content must be durable before its completed receipt. The underlying
# importer commits SQL first; a crash before this finishes remains started.
directories = [tree]
for path in tree.rglob("*"):
mode = path.lstat().st_mode
if stat.S_ISDIR(mode):
directories.append(path)
elif stat.S_ISREG(mode):
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
try:
os.fsync(fd)
finally:
os.close(fd)
else:
raise ValueError("Imported tree contains an unsupported file")
for directory in sorted(directories, key=lambda p: len(p.parts), reverse=True):
sync_directory(directory)
sync_directory(tree.parent)
sync_directory(tree.parent.parent)
class CorpusReceipts:
def __init__(self, directory, manifest, plan):
self.directory = Path(directory).expanduser().absolute()
private_path(self.directory, directory=True)
self.directory = self.directory.resolve()
root = pageops.content_root().resolve()
for protected in (root, *(tree for tree, _, _, _ in plan)):
if (self.directory.is_relative_to(protected)
or protected.is_relative_to(self.directory)):
raise ValueError("Receipt directory must be separate from source and content")
self.path = self.directory / "checkpoint.json"
self.plan = plan
self.source_hashes = [tree_digest(tree) for tree, _, _, _ in plan]
self.binding = digest({
"manifest": hashlib.sha256(Path(manifest).read_bytes()).hexdigest(),
"content_root": str(root),
"database": {key: connection.settings_dict.get(key)
for key in ("ENGINE", "HOST", "PORT", "NAME", "USER")},
"units": [
[str(tree), tenant, slug, source_hash,
[[str(member.pk), role] for member, role in grants]]
for (tree, tenant, slug, grants), source_hash
in zip(plan, self.source_hashes, strict=True)
],
})
def read(self):
private_path(self.directory, directory=True)
private_path(self.path)
if not self.path.exists():
return {"schema": SCHEMA, "binding": self.binding,
"spaces": [{"state": "pending"} for _ in self.plan]}
data = json.loads(self.path.read_bytes())
if (not isinstance(data, dict) or data.get("schema") != SCHEMA
or data.get("binding") != self.binding
or not isinstance(data.get("spaces"), list)
or len(data["spaces"]) != len(self.plan)):
raise ValueError("Receipt does not match this corpus, mapping or destination")
return data
def validate(self, data):
for unit, (_, tenant, slug, _) in zip(data["spaces"], self.plan, strict=True):
if not isinstance(unit, dict):
raise ValueError("Invalid space checkpoint")
state = unit.get("state")
if state == "completed":
try:
uuid.UUID(unit["space_id"])
if any(not re.fullmatch(r"[a-f0-9]{64}", unit[key])
for key in ("files_sha256", "space_sha256", "grants_sha256")):
raise ValueError
if any(type(unit[key]) is not int or unit[key] < 0
for key in ("pages", "assets")):
raise ValueError
except (KeyError, TypeError, ValueError, AttributeError) as exc:
raise ValueError("Invalid completed space checkpoint") from exc
# Never recreate an independently deleted/replaced space or infer
# that a later edit or grant change should be reversed.
if not Space.objects.filter(pk=unit.get("space_id"), tenant_id=tenant,
slug=slug).exists():
raise ValueError("Completed space identity changed; reconciliation required")
elif state == "pending":
if (Space.objects.filter(slug=slug).exists()
or pageops.space_dir(slug).exists()):
raise ValueError("Unreceipted destination exists; reconciliation required")
else:
raise ValueError(
"Uncertain space import; reconcile the started receipt before retry")
def save(self, data):
private_path(self.path)
fd, temporary = tempfile.mkstemp(prefix=".checkpoint-", dir=self.directory)
try:
with os.fdopen(fd, "w") as stream:
json.dump(data, stream, sort_keys=True)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, self.path)
sync_directory(self.directory)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
@contextmanager
def lock(self):
# Parent must already exist; do not chmod an operator's existing path.
self.directory.mkdir(mode=0o700, exist_ok=True)
private_path(self.directory, directory=True)
sync_directory(self.directory.parent)
lock = self.directory / ".lock"
private_path(lock)
fd = os.open(lock, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
try:
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
raise ValueError("Another importer owns this receipt directory") from exc
yield
finally:
os.close(fd)
def run(self):
if connection.in_atomic_block or not connection.get_autocommit():
raise ValueError("Receipted import requires independent database commits")
self.validate(self.read()) # Invalid inputs remain write-free.
with self.lock(), pageops.content_write_lock():
data = self.read()
self.validate(data)
pageops.recover_interrupted_content()
self.save(data)
imported = skipped = 0
for index, (tree, tenant, slug, grants) in enumerate(self.plan):
if data["spaces"][index]["state"] == "completed":
skipped += 1
continue
if tree_digest(tree) != self.source_hashes[index]:
raise ValueError("Source changed after preflight")
# Persist uncertainty BEFORE the first import write. Any failure,
# including a lost commit reply or receipt fsync, stays uncertain.
space_id = uuid.uuid4()
data["spaces"][index] = {"state": "started", "space_id": str(space_id)}
self.save(data)
report = import_space_tree(tree, tenant_id=tenant, slug=slug,
memberships=grants, replace=False,
new_space_id=space_id)
if tree_digest(tree) != self.source_hashes[index]:
raise ValueError("Source changed during import; reconciliation required")
space = Space.objects.get(tenant_id=tenant, slug=slug)
if space.pk != space_id:
raise ValueError("Imported space identity changed; reconciliation required")
observed_grants = sorted((str(m), role) for m, role in
SpaceMembership.objects.filter(space=space).values_list("member_id", "role"))
if observed_grants != sorted((str(m.pk), role) for m, role in grants):
raise ValueError("Imported grants differ from the reviewed mapping")
sync_tree(pageops.space_dir(slug))
# A fingerprint records the committed return, not acceptance of
# source fidelity or authorization to import more cohorts.
data["spaces"][index] = {
"state": "completed", "space_id": str(space.pk),
"files_sha256": tree_digest(pageops.space_dir(slug)),
"space_sha256": digest(Space.objects.filter(pk=space.pk).values().get()),
"grants_sha256": digest(observed_grants),
"pages": report.pages_copied, "assets": report.assets_copied,
}
self.save(data)
imported += 1
return imported, skipped

View file

@ -0,0 +1,72 @@
"""Explicit checkpoint completion for an exactly observed, already committed import.
No SQL/content repair or automatic retry. The deployment owner still supplies
admission, a paired backup and an external writer fence before invoking this.
"""
import hashlib
import re
from . import pageops
from .import_inspection import inspection_session, observe_unit
from .import_receipts import digest, sync_tree, tree_digest
from .models import Space
def complete_matching_commit(receipts, *, index, expected_inspection):
if (type(index) is not int or not 0 <= index < len(receipts.plan)
or not isinstance(expected_inspection, str)
or not re.fullmatch(r"[a-f0-9]{64}", expected_inspection)):
raise ValueError("An exact inspection digest and valid unit index are required")
with inspection_session(receipts) as (inspection, data):
if inspection["inspection_sha256"] != expected_inspection:
raise ValueError("Inspection changed; retain evidence and review again")
observed = inspection["spaces"][index]
if observed["classification"] != "reserved_destination_matches_source":
raise ValueError("Only an exact reserved committed import can be completed")
if inspection["unattributed_staging_entries"]:
raise ValueError("Unattributed staging requires owner reconciliation first")
tree, _, slug, _ = receipts.plan[index]
prior_unit = data["spaces"][index]
row = Space.objects.filter(pk=prior_unit["space_id"]).values().get()
if digest([row]) != observed["space_sha256"]:
raise ValueError("Observed SQL identity changed")
# The original importer can die before fsync or its completed checkpoint.
# Make the matching published tree durable before acknowledging it.
sync_tree(pageops.space_dir(slug))
if (observe_unit(receipts, index, prior_unit) != observed
or tree_digest(tree) != receipts.source_hashes[index]
or hashlib.sha256(receipts.path.read_bytes()).hexdigest()
!= inspection["checkpoint_sha256"]):
raise ValueError("State changed before checkpoint completion")
data["spaces"][index] = {
"state": "completed",
"space_id": prior_unit["space_id"],
"files_sha256": observed["files_sha256"],
"space_sha256": digest(row),
"grants_sha256": observed["grants_sha256"],
"pages": len(list((tree / "pages").glob("*.md"))),
"assets": sum(p.is_file() for p in (tree / "assets").rglob("*")),
"reconciliation": {
"schema": "coulomb.corpus-matching-commit-reconciliation.v1",
"prior_unit": prior_unit,
"inspection": inspection,
},
}
# Existing atomic replacement + fsync keeps the prior started checkpoint
# or the completed one with its evidence. Lost output requires readback;
# replay of the old inspection cannot acknowledge a different state.
receipts.save(data)
return {
"schema": "coulomb.corpus-matching-commit-return.v1",
"status": "matching_commit_checkpoint_completed",
"index": index,
"inspection_sha256": expected_inspection,
"checkpoint_sha256": hashlib.sha256(receipts.path.read_bytes()).hexdigest(),
"checkpoint_completed": True,
"sql_modified": False,
"content_modified": False,
"data_restored": False,
"external_writer_fence_verified": False,
"remaining_import_authorized": False,
}

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import shutil
import tempfile
import uuid
from dataclasses import dataclass, field
from pathlib import Path
@ -63,11 +64,15 @@ def _metadata(tree: Path, slug: str, title: str | None = None) -> tuple[str, str
def validate_space_tree(
tree: Path, *, tenant_id: str, slug: str | None = None,
member: Member | None = None, replace: bool = False, title: str | None = None,
check_existing: bool = True,
) -> tuple[Path, str]:
"""Preflight used by dry-run and execution, before any content writes.
CONTENT_ROOT is instance-local and keyed by slug, so importing a slug
already owned by another tenant is unsupported and must fail closed.
Receipt-aware preflight may set check_existing=False to inspect sources for
completed units; it must validate destination identities separately. The
actual importer always performs the default destination checks again.
"""
tree = tree.expanduser()
if tree.is_symlink():
@ -115,9 +120,9 @@ def validate_space_tree(
for path in (root, root / "spaces", dest):
if path.is_symlink():
raise pageops.PageOpsError("Destination must not be a symlink")
if dest.exists() and not replace:
if check_existing and dest.exists() and not replace:
raise pageops.PageOpsError("Destination already exists; pass replace=True to overwrite")
if dest.exists() and pageops.page_aliases(space_slug):
if check_existing and dest.exists() and pageops.page_aliases(space_slug):
raise pageops.PageOpsError(
"Retained page addresses need reconciliation before replacing content"
)
@ -128,7 +133,13 @@ def import_space_tree(
tree: Path, *, tenant_id: str, slug: str | None = None,
member: Member | None = None, title: str | None = None,
replace: bool = False, memberships: list[tuple[Member, str]] | None = None,
new_space_id: uuid.UUID | None = None,
) -> ImportReport:
if new_space_id is not None:
if replace or not isinstance(new_space_id, uuid.UUID):
raise pageops.PageOpsError("Reserved import identity requires a new UUID space")
if Space.objects.filter(pk=new_space_id).exists():
raise pageops.PageOpsError("Reserved import identity already exists")
# Invalid proposals remain write-free, including lock-file creation.
validate_space_tree(tree, tenant_id=tenant_id, slug=slug, member=member,
replace=replace, title=title)
@ -136,7 +147,8 @@ def import_space_tree(
if mapped.tenant_id != tenant_id or role not in SpaceMembership.Role.values:
raise pageops.PageOpsError("Invalid membership tenant or role")
return _import_space_tree(tree, tenant_id=tenant_id, slug=slug, member=member,
title=title, replace=replace, memberships=memberships)
title=title, replace=replace, memberships=memberships,
new_space_id=new_space_id)
@pageops.content_writer
@ -144,6 +156,7 @@ def _import_space_tree(
tree: Path, *, tenant_id: str, slug: str | None = None,
member: Member | None = None, title: str | None = None,
replace: bool = False, memberships: list[tuple[Member, str]] | None = None,
new_space_id: uuid.UUID | None = None,
) -> ImportReport:
"""Stage content, then publish with DB writes; restore on ordinary errors.
@ -190,15 +203,22 @@ def _import_space_tree(
try:
with transaction.atomic():
try:
space, created = Space.objects.update_or_create(
tenant_id=tenant_id, slug=space_slug,
defaults={"title": display_title, "description": abstractor,
"visual": visual, "is_active": True,
"content_root": "pages", "default_branch": "main"},
)
defaults = {"title": display_title, "description": abstractor,
"visual": visual, "is_active": True,
"content_root": "pages", "default_branch": "main"}
if new_space_id is not None:
# A receipted attempt owns only its pre-recorded UUID.
# Never update a row created concurrently at this slug.
space = Space.objects.create(id=new_space_id, tenant_id=tenant_id,
slug=space_slug, **defaults)
created = True
else:
space, created = Space.objects.update_or_create(
tenant_id=tenant_id, slug=space_slug, defaults=defaults)
except IntegrityError as exc:
raise pageops.PageOpsError(
"Content slug belongs to another tenant"
"Reserved import identity or destination already exists"
if new_space_id is not None else "Content slug belongs to another tenant"
) from exc
report.space_created = created
report.space_updated = not created

View file

@ -9,6 +9,7 @@ from django.core.management.base import BaseCommand, CommandError
from coulomb_social.apps.members.models import Member
from coulomb_social.apps.spaces import pageops
from coulomb_social.apps.spaces.import_receipts import CorpusReceipts
from coulomb_social.apps.spaces.import_tree import import_space_tree, validate_space_tree
from coulomb_social.apps.spaces.models import SpaceMembership
@ -20,10 +21,18 @@ class Command(BaseCommand):
parser.add_argument("manifest")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--replace", action="store_true")
parser.add_argument("--receipt-dir", help="Private durable checkpoints for new spaces only")
def handle(self, *args, **options):
if options["receipt_dir"] and options["replace"]:
raise CommandError("Receipted imports cannot replace existing spaces")
try:
plan = self.preflight(Path(options["manifest"]), replace=options["replace"])
plan = self.preflight(Path(options["manifest"]), replace=options["replace"],
check_existing=not options["receipt_dir"])
receipts = None
if options["receipt_dir"]:
receipts = CorpusReceipts(options["receipt_dir"], options["manifest"], plan)
receipts.validate(receipts.read())
except (ValueError, KeyError, TypeError, OSError, pageops.PageOpsError) as exc:
raise CommandError(f"Manifest preflight failed: {exc}") from exc
pages = sum(len(list((tree / "pages").glob("*.md"))) for tree, _, _, _ in plan)
@ -31,6 +40,17 @@ class Command(BaseCommand):
if options["dry_run"]:
self.stdout.write("Dry run — no writes")
return
if receipts is not None:
try:
imported, skipped = receipts.run()
except Exception as exc:
raise CommandError(
"Receipted import stopped; inspect the private checkpoint before retrying. "
"A started space has an uncertain outcome and must be reconciled."
) from exc
self.stdout.write(self.style.SUCCESS(
f"Imported {imported} space(s); skipped {skipped} completed space(s)"))
return
completed = 0
for tree, tenant, slug, grants in plan:
try:
@ -52,7 +72,7 @@ class Command(BaseCommand):
)
self.stdout.write(self.style.SUCCESS(f"Imported {completed} space(s)"))
def preflight(self, manifest: Path, *, replace: bool):
def preflight(self, manifest: Path, *, replace: bool, check_existing: bool = True):
manifest = manifest.expanduser().resolve()
data = json.loads(manifest.read_text(encoding="utf-8"))
if (not isinstance(data, dict) or data.get("version") != 1
@ -71,6 +91,7 @@ class Command(BaseCommand):
tree = manifest.parent / tree
tree, slug = validate_space_tree(
tree, tenant_id=tenant, slug=entry.get("slug"), replace=replace,
check_existing=check_existing,
)
if slug in slugs:
raise ValueError("Duplicate destination content slug")

View file

@ -0,0 +1,34 @@
"""Inspect uncertain receipt-backed imports without changing content or checkpoints."""
import json
from pathlib import Path
from django.core.management.base import BaseCommand, CommandError
from coulomb_social.apps.spaces import pageops
from coulomb_social.apps.spaces.import_inspection import inspect
from coulomb_social.apps.spaces.import_receipts import CorpusReceipts
from coulomb_social.apps.spaces.management.commands.import_content_corpus import (
Command as ImportCommand,
)
class Command(BaseCommand):
help = "Classify an existing corpus checkpoint; never repair or authorize a retry"
def add_arguments(self, parser):
parser.add_argument("manifest")
parser.add_argument("--receipt-dir", required=True)
def handle(self, *args, **options):
try:
manifest = Path(options["manifest"])
plan = ImportCommand().preflight(manifest, replace=False, check_existing=False)
receipts = CorpusReceipts(options["receipt_dir"], manifest, plan)
result = inspect(receipts)
except (ValueError, KeyError, TypeError, OSError, pageops.PageOpsError) as exc:
# Do not disclose source paths, identities or row contents in diagnostics.
raise CommandError(
"Corpus inspection refused; retain the checkpoint and inspect privately"
) from exc
self.stdout.write(json.dumps(result, sort_keys=True))

View file

@ -0,0 +1,42 @@
"""Record one matching committed import; never restore or delete application data."""
import json
from pathlib import Path
from django.core.management.base import BaseCommand, CommandError
from coulomb_social.apps.spaces.import_receipts import CorpusReceipts
from coulomb_social.apps.spaces.import_reconciliation import complete_matching_commit
from coulomb_social.apps.spaces.management.commands.import_content_corpus import (
Command as ImportCommand,
)
class Command(BaseCommand):
help = "Complete one exact matching import checkpoint after owner reconciliation review"
def add_arguments(self, parser):
parser.add_argument("manifest")
parser.add_argument("--receipt-dir", required=True)
parser.add_argument("--unit-index", type=int, required=True)
parser.add_argument("--expect-inspection", required=True)
parser.add_argument("--complete-matching-commit", action="store_true", required=True)
def handle(self, *args, **options):
if not options["complete_matching_commit"]:
raise CommandError("Explicit matching-commit completion is required")
try:
manifest = Path(options["manifest"])
plan = ImportCommand().preflight(manifest, replace=False, check_existing=False)
receipts = CorpusReceipts(options["receipt_dir"], manifest, plan)
result = complete_matching_commit(
receipts, index=options["unit_index"],
expected_inspection=options["expect_inspection"],
)
except Exception as exc:
# Failure can follow durable checkpoint replacement. Never advise replay
# or disclose source paths, identities or SQL values in the error.
raise CommandError(
"Reconciliation stopped; inspect the retained checkpoint before further action"
) from exc
self.stdout.write(json.dumps(result, sort_keys=True))

View file

@ -448,6 +448,9 @@ def _finish_transfer_record(record, source_path, dest_confirmed):
def _recover_record(record):
if isinstance(record, dict) and record.get("op") == "attachment_upload":
from .attachments import recover
return recover(record)
if record == "held" or not _record_is_actionable(record):
logger.error("content recovery left a journal for operator review")
return "held"
@ -661,7 +664,8 @@ _BODY_ASSET = re.compile(
)
def _copy_assets(source_space, dest_space, dest_page, visual, body, before_create=None):
def _copy_assets(source_space, dest_space, dest_page, visual, body, before_create=None,
attached=()):
"""Copy local Visual and Markdown link/image destinations as one staged set."""
from .assets import asset_parts, open_local_asset
@ -725,7 +729,8 @@ def _copy_assets(source_space, dest_space, dest_page, visual, body, before_creat
body = _BODY_ASSET.sub(
lambda match: match["prefix"] + copy_reference(match["url"]), body,
)
return visual, body, created
attached = [{**entry, "path": copy_reference(entry["path"])} for entry in attached]
return visual, body, created, attached
except BaseException:
for path in created:
path.unlink(missing_ok=True)
@ -764,8 +769,11 @@ def copy_page(
on_asset(name)
try:
visual, body, created = _copy_assets(
from .attachments import KEY, entries
visual, body, created, attached = _copy_assets(
source_space, dest_space, dest_page, src.visual, src.body, note_asset,
attached=entries(src.meta),
)
new_title = title if title is not None else (src.title or dest_page)
if title is None and new_title == src.title and source_space == dest_space:
@ -774,7 +782,8 @@ def copy_page(
dest_space, dest_page, title=new_title, abstractor=src.abstractor,
visual=visual, body=body, expected_revision="",
extra_meta={k: v for k, v in src.meta.items()
if k not in {"title", "abstractor", "visual", "space"}},
if k not in {"title", "abstractor", "visual", "space", KEY}}
| ({KEY: json.dumps(attached)} if KEY in src.meta else {}),
)
if record is not None:
record["dest_revision"] = doc.revision

View file

@ -34,6 +34,11 @@ def audit_body(body: str):
rendered = render_markdown(body)
before, after = _Structure(raw), _Structure(rendered)
removed_attrs = before.attributes - after.attributes
reference_attributes = {"href", "src", "poster", "data", "action"}
before_refs = Counter({key: count for key, count in before.attributes.items()
if key[1] in reference_attributes})
after_refs = Counter({key: count for key, count in after.attributes.items()
if key[1] in reference_attributes})
text_changed = " ".join("".join(before.text).split()) != (
" ".join("".join(after.text).split()))
markup_changed = before.tags != after.tags or before.attributes != after.attributes
@ -43,6 +48,11 @@ def audit_body(body: str):
"sanitization_changed_html": raw != rendered,
"markup_changed_by_sanitization": markup_changed,
"serialization_only_change": raw != rendered and not markup_changed and not text_changed,
"reference_occurrences_before": sum(before_refs.values()),
"reference_occurrences_after": sum(after_refs.values()),
"reference_attributes_preserved": before_refs == after_refs,
"removed_reference_occurrences": sum((before_refs - after_refs).values()),
"added_reference_occurrences": sum((after_refs - before_refs).values()),
"removed_tag_counts": dict(sorted((before.tags - after.tags).items())),
"added_tag_counts": dict(sorted((after.tags - before.tags).items())),
"removed_attributes": [

View file

@ -12,6 +12,9 @@ urlpatterns = [
),
path("new/", views.space_create, name="create"),
path("<slug:slug>/assets/<path:asset_path>", views.space_asset, name="asset"),
path("<slug:slug>/downloads/<path:asset_path>", views.space_download, name="download"),
path("<slug:slug>/pages/<slug:page_slug>/attachments/",
views.page_attachments, name="page_attachments"),
path("<slug:slug>/edit/", views.space_edit, name="edit"),
path("<slug:slug>/sharing/", views.space_sharing, name="sharing"),
path("<slug:slug>/archive/", views.space_archive, name="archive"),

View file

@ -2,6 +2,7 @@ import hashlib
import hmac
import json
import logging
from pathlib import PurePosixPath
from django.conf import settings
from django.contrib import messages
@ -21,8 +22,8 @@ from django.views.decorators.http import require_http_methods, require_POST, req
from coulomb_social.apps.core.principal import build_principal
from . import pageops
from .assets import local_asset_url, open_local_image
from . import attachments, pageops
from .assets import local_asset_url, open_local_asset, open_local_image
from .content import (
clear_content_cache_for_repo,
clear_content_cache_for_space,
@ -79,6 +80,81 @@ def space_asset(request: HttpRequest, slug: str, asset_path: str) -> HttpRespons
return response
@login_required
@require_safe
def space_download(request, slug, asset_path):
principal, forbid = _principal_or_forbid(request, "space_asset")
if forbid:
return forbid
space = get_space_for_member(principal.get("member"), slug)
if space is None or Space.objects.filter(slug=slug).exclude(pk=space.pk).exists():
return HttpResponseNotFound("Attachment not found.")
try:
file = open_local_asset(slug, asset_path)
except (OSError, ValueError):
return HttpResponseNotFound("Attachment not found.")
response = FileResponse(file, as_attachment=True, filename=PurePosixPath(asset_path).name,
content_type="application/octet-stream")
response["Cache-Control"] = "private, no-store"
response["X-Content-Type-Options"] = "nosniff"
response["Content-Security-Policy"] = "default-src 'none'; sandbox"
return response
@login_required
@require_http_methods(["GET", "POST"])
def page_attachments(request, slug, page_slug):
principal, forbid = _principal_or_forbid(
request, "page_edit" if request.method == "POST" else "space_detail")
if forbid:
return forbid
member = principal.get("member")
space = get_space_for_member(member, slug)
if space is None:
return HttpResponseNotFound("Page not found.")
can_write = can_write_space(member, space)
if request.method == "POST" and not can_write:
return HttpResponseForbidden("You have read-only access to this space.")
try:
doc = pageops.read_page(slug, page_slug)
if not doc.exists:
return HttpResponseNotFound("Page not found.")
if request.method == "POST":
try:
length = int(request.META.get("CONTENT_LENGTH") or 0)
except (ValueError, TypeError):
return HttpResponse("Invalid upload length.", status=400)
if length < 0:
return HttpResponse("Invalid upload length.", status=400)
if length > attachments.MAX_UPLOAD_BYTES + 65536:
return HttpResponse("Choose a file no larger than 16 MiB.", status=413)
action = request.POST.get("action")
if action == "add":
attachments.add(slug, page_slug, request.FILES.get("file"),
revision=request.POST.get("revision"))
messages.success(request, "Attachment added.")
elif action == "remove":
attachments.remove(slug, page_slug, request.POST.get("reference"),
revision=request.POST.get("revision"))
messages.success(
request, "Attachment removed from this page. Earlier revisions retain it.")
else:
raise pageops.PageOpsError("Choose an attachment action")
clear_content_cache_for_space(space)
return redirect("spaces:page_attachments", slug=slug, page_slug=page_slug)
files = attachments.page_files(slug, doc)
except (pageops.PageOpsError, OSError) as exc:
messages.error(request, _mutation_error(exc))
if request.method == "POST":
return redirect("spaces:page_attachments", slug=slug, page_slug=page_slug)
return HttpResponse("Attachments unavailable.", status=409)
response = render(request, "spaces/attachments.html", {
"space": space, "doc": doc, "files": files, "can_write": can_write,
})
response["Cache-Control"] = "private, no-store"
return response
def _principal_or_forbid(request: HttpRequest, resource: str):
principal = build_principal(request, authz_resource_id=resource)
if not principal["authz_allow"]:
@ -301,6 +377,14 @@ def page_edit(request: HttpRequest, slug: str, page_slug: str = "index") -> Http
return redirect("spaces:detail", slug=slug)
doc = pageops.read_page(space.slug, page_slug)
# Show files from the saved page even when a rejected edit redisplays the
# submitted fields. Attachment mutations keep their own revision checks.
try:
saved_files = attachments.page_files(space.slug, doc) if doc.exists else []
attachment_error = ""
except (pageops.PageOpsError, OSError) as exc:
saved_files = []
attachment_error = _mutation_error(exc)
if request.method == "POST":
title = (request.POST.get("title") or "").strip()
abstractor = (request.POST.get("abstractor") or "").strip()
@ -345,6 +429,8 @@ def page_edit(request: HttpRequest, slug: str, page_slug: str = "index") -> Http
"submitted_slug": request.POST.get("slug", page_slug),
"is_new": not doc.exists,
"form_title": "Edit or rename page" if doc.exists else "New page",
"saved_files": saved_files,
"attachment_error": attachment_error,
},
)

View file

@ -0,0 +1,49 @@
{% extends "base.html" %}
{% block title %}Attachments — {{ doc.title }}{% endblock %}
{% block content %}
<p><a href="{% url 'spaces:detail' space.slug %}?page={{ doc.slug }}">← {{ doc.title }}</a></p>
<h1>Attachments</h1>
<p class="muted">Files for {{ doc.title }}. Access follows this space’s sharing settings.</p>
<div class="card">
<ul>
{% for file in files %}
<li style="margin:1rem 0;">
<strong>{{ file.name }}</strong>
{% if file.open_url %}<a class="btn secondary small" href="{{ file.open_url }}" target="_blank" rel="noopener">Open image</a>{% endif %}
<a class="btn secondary small" href="{{ file.download_url }}">Download</a>
{% if can_write %}
{% if file.managed %}
<form method="post" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="action" value="remove">
<input type="hidden" name="revision" value="{{ doc.revision }}">
<input type="hidden" name="reference" value="{{ file.path }}">
<button class="btn secondary small" type="submit">Remove from page</button>
</form>
{% else %}
<a href="{% url 'spaces:page_edit' space.slug doc.slug %}">Edit page reference</a>
{% endif %}
{% endif %}
</li>
{% empty %}
<li>No attachments on this page.</li>
{% endfor %}
</ul>
</div>
{% if can_write %}
<div class="card">
<h2>Add attachment</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="action" value="add">
<input type="hidden" name="revision" value="{{ doc.revision }}">
<p><label for="attachment-file">File (up to 16 MiB)</label><br>
<input id="attachment-file" name="file" type="file" required></p>
<button class="btn" type="submit">Add attachment</button>
</form>
<p class="muted">Removing a file from this page keeps it in earlier revisions. Existing links and visuals can be changed in the page editor.</p>
</div>
{% else %}
<p class="muted">Read-only access. Authors can add or remove attachments.</p>
{% endif %}
{% endblock %}

View file

@ -109,6 +109,12 @@
title="Copy the page content to paste into an email" aria-controls="copy-content-status">
Copy content to clipboard
</button>
{% if page.source == 'content-plane' %}
<details>
<summary class="btn secondary small">Attachments</summary>
<p><a href="{% url 'spaces:page_attachments' space.slug page.slug %}">{% if can_write %}Open, download or manage attachments{% else %}Open or download attachments{% endif %}</a></p>
</details>
{% endif %}
{% endif %}
{% if can_write %}
<a class="btn small" href="{% url 'spaces:page_edit' space.slug page.slug|default:'index' %}">Edit page</a>

View file

@ -32,6 +32,30 @@
<h1>{{ form_title }}</h1>
<p class="muted">Change the title to rename the displayed protocol. Change the file name below to rename its Markdown file and page address.</p>
{% if not is_new %}
<section class="card" aria-labelledby="editor-attachments-title">
<h2 id="editor-attachments-title">Attachments</h2>
<p class="muted">Files on the saved page. Editing or renaming the page keeps its attachments.</p>
{% if attachment_error %}
<p>{{ attachment_error }}</p>
{% else %}
<ul>
{% for file in saved_files %}
<li>
<strong>{{ file.name }}</strong>
{% if file.open_url %}<a href="{{ file.open_url }}" target="_blank" rel="noopener">Open image</a>{% endif %}
<a href="{{ file.download_url }}">Download</a>
</li>
{% empty %}
<li>No attachments on this page.</li>
{% endfor %}
</ul>
{% endif %}
<a href="{% url 'spaces:page_attachments' space.slug doc.slug %}" target="_blank" rel="noopener">Manage attachments (new tab)</a>
<p class="muted">Save your edits before adding or removing files, then reopen the editor.</p>
</section>
{% endif %}
<form method="post" class="card" style="margin-top:1rem;">
{% csrf_token %}
<input type="hidden" name="revision" value="{{ doc.revision }}">

View file

@ -142,3 +142,101 @@ Set COULOMB_SOCIAL_CORPUS_DIR to a private directory if needed. Do not put raw
exports in the checkout. Default target route is /vw_pages; override via
COULOMB_SOCIAL_CAPTURE_ROUTE only after confirming the actual authenticated
protocol surface. A successful public-route load is not authentication proof.
## Retained Visual capture and review bundle
The October31 all-space bundle now has a separate private Visual preparation at
`~/.config/coulomb-social/auth/exports/visual-bundle-20261031`. The original
`review-bundle-20261031` remains unchanged.242Visual references were captured from
108URLs on the one recorded Bubble CDN, producing90distinct images.236draft
article pages and67space indexes link local hashed assets. All90images decode in
an offline browser; this is not a source/product appearance verdict or an import.
The reusable collector is `scripts/capture-bubble-visuals.py ORIGINAL --output NEW`.
It requires a complete hash-bound original bundle, a new private directory outside
repositories, and the exact known CDN over HTTPS. It refuses redirects, alternate
origins, credentials in URLs, active/unknown image formats, source drift, oversized
responses and existing output. No stored browser credentials are read or sent.
A missing final reconciliation.json means partial output, which must be retained
for review rather than used as an import bundle. Original body/date bytes are not
rewritten; only the compiler's empty Visual metadata is replaced.
Verification for the completed bundle (read-only):
```bash
python3 scripts/verify-bubble-visual-preservation.py \
~/.config/coulomb-social/auth/exports/review-bundle-20261031 \
~/.config/coulomb-social/auth/exports/visual-bundle-20261031
node scripts/verify-bubble-visuals.mjs \
~/.config/coulomb-social/auth/exports/visual-bundle-20261031
```
Only aggregate results belong in Git/chat. Images, titles, source URLs and original
records remain private. The new bundle retains all71spaces and the existing
graph/grant/fidelity gates.18body links and one attachment flag are still
unreconciled. Captured Visual bytes do not authorize copying memberships or
productive import. Public proof: docs/release/bubble-visual-preparation-2026-10-31.json.
## Private access reconciliation
`scripts/audit-bubble-access.py ORIGINAL NEW_PRIVATE_DIRECTORY` inventories the
retained source ownership, membership and visibility fields without proposing
target grants. It requires private, hash-bound source snapshots and refuses
source drift, symlinks, public files, hard links, existing output and output
inside the original bundle or this repository. The JSON and escaped, network-free
HTML review are private; stdout contains aggregate counts and hashes only.
The October 31 review is at
`~/.config/coulomb-social/auth/exports/access-review-20261031/index.html`.
It covers 70 fresh spaces, 245 article records and one inventory-only space.
Three distinct source account references remain to be linked through authoritative
identity evidence. Nine creator references are unresolved. A missing source
membership field is unknown, not an empty list. Source role names and visibility
values are preserved for review; no equivalence with target owner/member/viewer
permissions is assumed. The accepted ReichelAG owner grant remains evidence for
that cohort alone. See `docs/release/bubble-access-audit-2026-10-31.json`.
## Body links and deferred scrap relations
`scripts/verify-bubble-body-resources.py ORIGINAL VISUAL --output PRIVATE_JSON`
checks source hashes, recompiles original draft bytes, checks prepared bodies and
compares reference attributes before and after the product sanitizer. It neither
fetches destinations nor changes source files. Its stdout receipt contains only
aggregate counts and hashes. Raw references and deferred scrap descriptors belong
in the separate private report, outside both bundles and the checkout.
The October 31 proof verifies 18 distinct body-link entries with 28 occurrences,
all preserved by the product renderer. It also verifies 242 source bodies and 236
prepared pages. This does not prove remote availability or Bubble appearance.
Four `_list_custom_scrap` values are deferred strings, not captured record lists;
the compiler now preserves them as unresolved review flags instead of silently
omitting them. Explicit empty lists remain distinct from missing or deferred data.
These four records and the separate attachment-flagged article still need source
recovery or disposition. Do not interpret the strings as article IDs or infer
that the attachment flag enumerates all resources. The supplemental report is
`~/.config/coulomb-social/auth/exports/body-resources-20261031.json`; the existing
original and Visual bundle receipts remain unchanged. Public proof:
`docs/release/bubble-body-resources-2026-10-31.json`.
### Native resource follow-up
The separate `resources-20261031` private directory records a bounded source
observation of the five affected articles. Their fresh parent records match the
retained capture, but the page visits returned no typed scrap/attachment records.
Two response reads failed, so zero captured records must not be read as zero
existing resources. No prior capture was overwritten. The private `index.html`
links all five original pages for the operator to identify the existing-resource
view/download path. Add attachment controls were observed but not activated.
The collector is `scripts/capture-bubble-resources.mjs`, taking `--articles`,
`--expect-articles-sha256` and a new private `--output` directory. It visits at
most ten explicit affected article IDs, selects only typed scrap/attachment
content envelopes from the existing same-origin read responses, and excludes
user/session envelopes. Multiple observed resource versions remain separate;
empty initialization envelopes remain unresolved. It does not decode or invent
provider query payloads, click controls, submit forms, fetch arbitrary attachment
URLs or create an import manifest. `completed_bounded_observation` describes only
the terminal visit pass; completeness remains false. Source hashes, response
failures and private checkpoint hashes are in
`docs/release/bubble-resource-capture-2026-10-31.json`.

View file

@ -0,0 +1,32 @@
{
"schema": "coulomb.attachment-menu-source-validation.v1",
"product_decision": "78175c34-58e1-4ad6-944d-0662c0462ae6",
"full_main_gate": {
"command": "TMPDIR=/var/tmp make check",
"passed": 670,
"failures": 0,
"errors": 0,
"skipped": 0,
"log_sha256": "357547d34f7d73f750721338cff3e07439e8c710d0f1cf838541c1b175268335"
},
"isolated_release_gate": {
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"command": "make check",
"passed": 568,
"failures": 0,
"errors": 0,
"skipped": 0,
"log_sha256": "d9a9c8883f0d4021918c8600f9b5055cac24ca6b9943ee89db067f55d65ccdb1"
},
"checks": [
"Author upload/remove; authorized reader download; foreign-tenant, outsider, revoked, archived and anonymous denial",
"Stale page and CSRF refusal; immutable file names; 16 MiB limit; traversal and symlink denial",
"Retained body/source dates and associations through edit/rename; removal keeps historical revision bytes",
"Independent copy/move associations and managed files through 14 abrupt process deaths",
"Upload recovery at partial bytes, complete bytes, stored revision and published page",
"Real loopback browser upload/download/copy/move/remove and permission checks; no external requests"
],
"synthetic_fixtures_only": true,
"production_deployed": false,
"productive_user_acceptance": false
}

View file

@ -0,0 +1,280 @@
{
"schema": "coulomb.completed-evidence-custody-independent-return.v1",
"verified_at": "2026-10-31T16:37:46.139987+00:00",
"proposal_digest": "sha256:fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611",
"receipt_sha256": "b79b26b1572faff0e2f2257e39e438bc065a4d2c71f92d879749e0e8b4b03493",
"receipt": {
"bytes_modified": false,
"capture_executed": false,
"changes": [
{
"after": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 543834,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793430825511133103,
"nlink": 4,
"relative_path": "releases",
"sha256": "3190439b978096a16c28aa0dd6b372735594808217b59a01dadbbe744d4110c8",
"uid": 0
},
"before": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 543834,
"kind": "directory",
"mode": "0o755",
"mtime_ns": 1793430825511133103,
"nlink": 4,
"relative_path": "releases",
"sha256": "3190439b978096a16c28aa0dd6b372735594808217b59a01dadbbe744d4110c8",
"uid": 0
},
"path": "releases"
},
{
"after": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549330,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793430825511133103,
"nlink": 3,
"relative_path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/charts",
"sha256": "6b8295661773cb5820c305091679383b09fb0d7dcdde4412eeae48bfabab28b5",
"uid": 0
},
"before": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549330,
"kind": "directory",
"mode": "0o755",
"mtime_ns": 1793430825511133103,
"nlink": 3,
"relative_path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/charts",
"sha256": "6b8295661773cb5820c305091679383b09fb0d7dcdde4412eeae48bfabab28b5",
"uid": 0
},
"path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/charts"
},
{
"after": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549341,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793430825512133101,
"nlink": 3,
"relative_path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/release",
"sha256": "25a35515bc386a0c0a1a91b5fa3c966fffad64e2ce1d8bde35959bc164b11d79",
"uid": 0
},
"before": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549341,
"kind": "directory",
"mode": "0o755",
"mtime_ns": 1793430825512133101,
"nlink": 3,
"relative_path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/release",
"sha256": "25a35515bc386a0c0a1a91b5fa3c966fffad64e2ce1d8bde35959bc164b11d79",
"uid": 0
},
"path": "releases/9f1daf6c5fb56b2b4fc9817f28f5ad090c884b0295f7d6f4469e42d48fa8d59f/capsule/release"
},
{
"after": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549219,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793406468579820277,
"nlink": 3,
"relative_path": "releases/5fd7ccd8f68057346367d81efb82b48280a16dd31d87683a5bd83e60e3d1837e/capsule/charts",
"sha256": "6b8295661773cb5820c305091679383b09fb0d7dcdde4412eeae48bfabab28b5",
"uid": 0
},
"before": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549219,
"kind": "directory",
"mode": "0o755",
"mtime_ns": 1793406468579820277,
"nlink": 3,
"relative_path": "releases/5fd7ccd8f68057346367d81efb82b48280a16dd31d87683a5bd83e60e3d1837e/capsule/charts",
"sha256": "6b8295661773cb5820c305091679383b09fb0d7dcdde4412eeae48bfabab28b5",
"uid": 0
},
"path": "releases/5fd7ccd8f68057346367d81efb82b48280a16dd31d87683a5bd83e60e3d1837e/capsule/charts"
},
{
"after": {
"bytes": 4096,
"device": 2049,
"gid": 0,
"hash_kind": "directory-entry-names",
"inode": 549263,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793412565567095985,
"nlink": 2,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private",
"sha256": "384eaf76c1a05ea25236c7a0d7bee390638b68fc89186604def37028364992fb",
"uid": 0
},
"before": {
"bytes": 4096,
"device": 2049,
"gid": 10001,
"hash_kind": "directory-entry-names",
"inode": 549263,
"kind": "directory",
"mode": "0o700",
"mtime_ns": 1793412565567095985,
"nlink": 2,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private",
"sha256": "384eaf76c1a05ea25236c7a0d7bee390638b68fc89186604def37028364992fb",
"uid": 10001
},
"path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private"
},
{
"after": {
"bytes": 455,
"device": 2049,
"gid": 0,
"hash_kind": "file-bytes",
"inode": 549311,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412565564096058,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/result.json",
"sha256": "859e5a80442ad77b5eff61b68e5062a167d51f5db1b6c2d1ee064db977beb022",
"uid": 0
},
"before": {
"bytes": 455,
"device": 2049,
"gid": 10001,
"hash_kind": "file-bytes",
"inode": 549311,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412565564096058,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/result.json",
"sha256": "859e5a80442ad77b5eff61b68e5062a167d51f5db1b6c2d1ee064db977beb022",
"uid": 10001
},
"path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/result.json"
},
{
"after": {
"bytes": 12930,
"device": 2049,
"gid": 0,
"hash_kind": "file-bytes",
"inode": 549315,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412565062108322,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/journal.json",
"sha256": "fef62ad9317066d5d82eccbea0317eceb2f883e07f50935104e47113855fe0dd",
"uid": 0
},
"before": {
"bytes": 12930,
"device": 2049,
"gid": 10001,
"hash_kind": "file-bytes",
"inode": 549315,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412565062108322,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/journal.json",
"sha256": "fef62ad9317066d5d82eccbea0317eceb2f883e07f50935104e47113855fe0dd",
"uid": 10001
},
"path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/journal.json"
},
{
"after": {
"bytes": 0,
"device": 2049,
"gid": 0,
"hash_kind": "file-bytes",
"inode": 549310,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412563167160722,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/execute.lock",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"uid": 0
},
"before": {
"bytes": 0,
"device": 2049,
"gid": 10001,
"hash_kind": "file-bytes",
"inode": 549310,
"kind": "regular",
"mode": "0o600",
"mtime_ns": 1793412563167160722,
"nlink": 1,
"relative_path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/execute.lock",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"uid": 10001
},
"path": "date-operations/47829c7ac2e6fd2c81fde1c914aec7c6e433098fa66985db6cfb9b34069942dc/private/execute.lock"
}
],
"data_deleted": false,
"paths_moved": false,
"postflight": {
"historical_operations_still_terminal": true,
"state_bytes": 11259904,
"whole_state_custody_guard_passed": true
},
"proposal_digest": "sha256:fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611",
"schema": "coulomb.completed-evidence-custody.v1",
"started_at": "2026-10-31T16:36:38.149305+00:00",
"status": "completed_evidence_private_root_custody_verified",
"timers_activated": false,
"updated_at": "2026-10-31T16:36:38.939513+00:00",
"verified_entries": 8
},
"native_entries_verified": 8,
"all_historical_hashes_inodes_paths_mtimes_preserved": true,
"whole_state_custody_guard_passed": true,
"state_bytes": 11259904,
"verification_read_only": true,
"verifier_source_sha256": "27fb56bb621ab9141f6b7ae205790e8a7eeb1618e2628940b3b6bc5525b4393d"
}

View file

@ -1,11 +1,19 @@
# Completed backup evidence custody repair
This is the exact proposed repair for the eight historical metadata exceptions
recorded in CSOC-WP-0012-T04. Native preflight passes. **Nothing has been changed.**
recorded in CSOC-WP-0012-T04. **Founder execution and independent postflight are
complete, under T15. Do not rerun the seal command.**
Proposal:
`sha256:fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611`.
State Hub decision `3ec2f98e-9db3-47d9-bc1a-0387f980b81c` is resolved from the
founder's actual execution at16:36:38Z. Independent read-only verification at
16:37:46Z confirms all eight intended deltas and unchanged hashes, paths, inodes
and mtimes. The whole-state custody guard passes at11,259,904bytes. Evidence:
`backup-completed-evidence-custody-return-2026-10-31.json`.
`operator-handoff-check-2026-10-31.json` retains the earlier pre-execution check.
## Exact change
Within `/var/lib/coulomb-social-backup/`, change:
@ -33,9 +41,9 @@ The strict backup capacity guard is unchanged. Successful execution also runs it
existing state-usage/custody check over the entire state tree. This is neither a
blanket chmod/chown nor an exception that omits historical data from accounting.
## Founder terminal command
## Historical founder terminal command — completed
Run this from the workstation after reviewing the scope above:
Retained for provenance; do not run again:
```bash
python3 /home/worsch/coulomb-social/scripts/seal-backup-evidence.py \
@ -48,9 +56,9 @@ At the prompt, enter:
SEAL sha256:fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611
```
Expected status: `completed_evidence_private_root_custody_verified`, eight entries
verified and `postflight.whole_state_custody_guard_passed: true`. Paste the result
back for independent readback. The private durable receipt is:
Recorded status: `completed_evidence_private_root_custody_verified`, eight entries
verified and `postflight.whole_state_custody_guard_passed: true`. Independent
readback is complete. The private durable receipt is:
`/var/lib/coulomb-social-backup-custody/fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611/receipt.json`.
@ -106,12 +114,12 @@ Nineteen dedicated cases exercise real metadata/lock IO, preserved bytes/paths,
explicit/partial rollback, interference refusal, source/digest boundaries,
nonterminal refusal, and native receipt/process/helper/mount/cluster/timer guards.
This resolves only the current custody mismatch after attended success. T04 still
The verified attended execution resolves the historical custody mismatch. T04 still
owns retention/capacity admission and historical-copy disposition. Preventing
future release/date producers from reintroducing these metadata mismatches is also
live T04 work before recurring service acceptance: nested release directory
creation currently uses default intermediate modes, and date helper journals
retain the execution UID after completion. Do not silently change frozen capsules.
required before recurring service acceptance. That source correction is now
completed under T13/RAPPS-WP-0015-T06; it does not repair the historical entries
listed here or change frozen capsules.
T07 still needs the existing route execution; T08 then needs inactive installation
and independent readback. No native backup-operation or migration acceptance is
inferred from this repair.

View file

@ -0,0 +1,371 @@
{
"schema": "coulomb.backup-installation-independent-postflight.v1",
"verified_at": "2026-10-31T17:30:18.219708+00:00",
"status": "installed_files_and_inactive_units_verified",
"proposal_digest": "sha256:8f442bf0ebed8bbf3b7f227b52081df52613f3f5f6874c4a59511cfc2ba06d42",
"receipt_sha256": "96329afbd5662f71bf6d6b05c003f0810be12c10036f9cadb41cc373d3a1108b",
"receipt": {
"schema": "coulomb.backup-source-supervised-installation.v1",
"proposal_digest": "sha256:8f442bf0ebed8bbf3b7f227b52081df52613f3f5f6874c4a59511cfc2ba06d42",
"status": "sources_installed_units_inactive_verified",
"preflight": {
"units": {
"coulomb-social-paired-backup.service": {
"LoadState": "not-found",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "",
"UnitFileState": ""
},
"coulomb-social-paired-backup.timer": {
"LoadState": "not-found",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "",
"UnitFileState": ""
},
"coulomb-social-backup-freshness.service": {
"LoadState": "not-found",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "",
"UnitFileState": ""
},
"coulomb-social-backup-freshness.timer": {
"LoadState": "not-found",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "",
"UnitFileState": ""
},
"coulomb-social-backup-alert@coulomb-social-paired-backup.service.service": {
"LoadState": "not-found",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "",
"UnitFileState": ""
}
},
"capacity": {
"state_bytes": 11259904,
"state_reserve_bytes": 7532969984,
"disk_reserve_bytes": 8606711808,
"free_bytes": 115579949056,
"free_inodes": 24523113,
"filesystem_blocks_reserved": false
},
"sdk": {
"boto3": "1.40.18",
"botocore": "1.40.76"
},
"route": {
"helm_revision": 6,
"runtime_and_complete_resources_verified": true
}
},
"timers_activated": false,
"capture_executed": false,
"email_sent": false,
"retained_data_deleted": false,
"started_at": "2026-10-31T17:22:48.757530+00:00",
"capacity_profile": {
"maximum_new_file_bytes": 1073741824,
"state_budget_bytes": 8589934592,
"minimum_free_bytes": 8589934592,
"minimum_free_inodes": 1024,
"control_margin_bytes": 16777216
},
"founder_confirmed_profile_and_installation": true,
"route_postflight": {
"helm_revision": 6,
"runtime_and_complete_resources_verified": true
},
"verified_targets": 19,
"units": {
"coulomb-social-paired-backup.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-paired-backup.service",
"UnitFileState": "static"
},
"coulomb-social-paired-backup.timer": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-paired-backup.timer",
"UnitFileState": "disabled"
},
"coulomb-social-backup-freshness.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-freshness.service",
"UnitFileState": "static"
},
"coulomb-social-backup-freshness.timer": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-freshness.timer",
"UnitFileState": "disabled"
},
"coulomb-social-backup-alert@coulomb-social-paired-backup.service.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-alert@.service",
"UnitFileState": "static"
}
},
"updated_at": "2026-10-31T17:22:54.560675+00:00"
},
"nested_receipt_sha256": "795d9eb672b24495c77ad636a210c5d2022c8c3124d276920db7eda02257e519",
"verified_targets": 19,
"installed_fingerprints": {
"/opt/coulomb-social-backup/paired-backup.py": {
"exists": true,
"sha256": "d161c742d1a6a182460a57d0a1719591ab58fc94006f55169ad3d74442a93630",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 534009,
"device": 2049,
"mtime_ns": 1793467368826077309
},
"/opt/coulomb-social-backup/snapshot-content-and-db.py": {
"exists": true,
"sha256": "dcc8dd2a3989f279597595f9260b31b24fcc9b6dc787f04c17ef0491c53d830d",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549255,
"device": 2049,
"mtime_ns": 1793430923413704430
},
"/opt/coulomb-social-backup/mirror-paired-backup.py": {
"exists": true,
"sha256": "9ceb26d9083af016c4b787907e4dce7bdf437c782fb2d15eb28d9e334e6e431c",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549201,
"device": 2049,
"mtime_ns": 1793467368831077393
},
"/opt/coulomb-social-backup/check-paired-backup-health.py": {
"exists": true,
"sha256": "2d40ec6e4c59ea7fe1d46065d197b9a87ca6811f023cc6f3e20e4184a3490566",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549309,
"device": 2049,
"mtime_ns": 1793467368836077477
},
"/opt/coulomb-social-backup/backup_capacity.py": {
"exists": true,
"sha256": "cb699a79e67b129239b5aa5d6f3b0d4ffaa9773a8b073227ad17fdecb5b9d893",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549354,
"device": 2049,
"mtime_ns": 1793467368843077594
},
"/opt/coulomb-social-backup/mirror-platform-runtime.json": {
"exists": true,
"sha256": "9253c5f6e956b67f499d3e12d2851be7fabd3bc56b00496b8810bbd0be8209f1",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549355,
"device": 2049,
"mtime_ns": 1793467368847077662
},
"/opt/coulomb-social-backup/backup_alerts.py": {
"exists": true,
"sha256": "07522437d4276b92f0a44149adc6c9e81bb18dbe59ce0f1f9e58a48347700d0e",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549359,
"device": 2049,
"mtime_ns": 1793467368887078332
},
"/opt/coulomb-social-backup/notify-backup-alert.py": {
"exists": true,
"sha256": "0bd53bc5ad22d8bcb0326fb18a17737744b06416fe785dad077308e45f051d5a",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549360,
"device": 2049,
"mtime_ns": 1793467368891078399
},
"/opt/coulomb-social-backup/observe-backup-alerts.py": {
"exists": true,
"sha256": "17caf4fa65283242f618b039ff1d018801c9890ea2a9178912cbdd4b50ee2664",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549361,
"device": 2049,
"mtime_ns": 1793467368895078466
},
"/opt/railiance-platform/scripts/application_recovery_identities.py": {
"exists": true,
"sha256": "20f56255ed6eeec4aafe33f142bc3ee03f623d4957a7b0cabb741d340f15f79d",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549356,
"device": 2049,
"mtime_ns": 1793467368852077745
},
"/opt/railiance-platform/scripts/application_recovery_mirror.py": {
"exists": true,
"sha256": "d48ab95e89b9eeae36269d853ebe4c44c22e1f57d4e14224f03d0b9aeeb34178",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549357,
"device": 2049,
"mtime_ns": 1793467368857077829
},
"/opt/railiance-platform/scripts/application_recovery_resources.py": {
"exists": true,
"sha256": "ab70ec341e738272730eb0daf76840d0bca807caeb3c4e4c2357f162fbed4498",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 549358,
"device": 2049,
"mtime_ns": 1793467368862077913
},
"/opt/railiance-platform/scripts/primary_application_archive.py": {
"exists": true,
"sha256": "a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 540610,
"device": 2049,
"mtime_ns": 1791537447437616251
},
"/etc/systemd/system/coulomb-social-backup-freshness.service": {
"exists": true,
"sha256": "8d6278c2cbca7f48b659c239c1d1e181fa4b9cf023fe7429850e294c6cd84d0e",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 19366,
"device": 2049,
"mtime_ns": 1793467368868078014
},
"/etc/systemd/system/coulomb-social-paired-backup.service": {
"exists": true,
"sha256": "8b01b788bb08f453fdd4807104185c89411e0b0b4dedee9240f14f110ceb5513",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 14867,
"device": 2049,
"mtime_ns": 1793467368873078098
},
"/etc/systemd/system/coulomb-social-backup-freshness.timer": {
"exists": true,
"sha256": "75d1d0721c07fdd0703910c06bb8a678396440e7d95411a37ee7de4938f3ac31",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 19367,
"device": 2049,
"mtime_ns": 1793467368878078181
},
"/etc/systemd/system/coulomb-social-paired-backup.timer": {
"exists": true,
"sha256": "5e87235e66f89a64a972562d6f163f62420628fc44fb7fd8d603a96c347dfbb9",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 19368,
"device": 2049,
"mtime_ns": 1793467368882078248
},
"/etc/systemd/system/coulomb-social-backup-alert@.service": {
"exists": true,
"sha256": "4bfa019d29b1d0de278f03553602a5e824de50ed99ebcf3b5035cf4d5b06fe98",
"mode": 420,
"uid": 0,
"gid": 0,
"inode": 19370,
"device": 2049,
"mtime_ns": 1793467368899078533
},
"/etc/coulomb-social-backup/alerting.env": {
"exists": true,
"sha256": "509844b5c8ef618b812d52f32e19c3c559623636dca62f7b16f437aa90cc0458",
"mode": 384,
"uid": 0,
"gid": 0,
"inode": 19369,
"device": 2049,
"mtime_ns": 1793467368904078617
}
},
"units": {
"coulomb-social-paired-backup.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-paired-backup.service",
"UnitFileState": "static"
},
"coulomb-social-paired-backup.timer": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-paired-backup.timer",
"UnitFileState": "disabled"
},
"coulomb-social-backup-freshness.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-freshness.service",
"UnitFileState": "static"
},
"coulomb-social-backup-freshness.timer": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-freshness.timer",
"UnitFileState": "disabled"
},
"coulomb-social-backup-alert@coulomb-social-paired-backup.service.service": {
"LoadState": "loaded",
"ActiveState": "inactive",
"SubState": "dead",
"FragmentPath": "/etc/systemd/system/coulomb-social-backup-alert@.service",
"UnitFileState": "static"
}
},
"route": {
"helm_revision": 6,
"runtime_and_complete_resources_verified": true
},
"capacity": {
"state_bytes": 11259904,
"state_reserve_bytes": 7532969984,
"disk_reserve_bytes": 8606711808,
"free_bytes": 115555618816,
"free_inodes": 24523041,
"filesystem_blocks_reserved": false
},
"sdk": {
"boto3": "1.40.18",
"botocore": "1.40.76"
},
"runtime_modified_by_verification": false
}

View file

@ -0,0 +1,29 @@
{
"schema": "coulomb.backup-route-independent-postflight.v1",
"verified_at": "2026-10-31T17:19:36.926769+00:00",
"status": "durable_receipt_and_live_route_verified",
"execution_digest": "sha256:4d8481061dc9ce6835169defc96887945052332a0db8d4234ae84032d63cc328",
"receipt": {
"schema": "rapp-telemetry.backup-route-execution.v1",
"execution_digest": "sha256:4d8481061dc9ce6835169defc96887945052332a0db8d4234ae84032d63cc328",
"proposal_digest": "sha256:4bcad54f7b8e1e77b94112e452cbb58eaf8e882dc10305129c5f9ca69f014a66",
"status": "backup_email_route_deployed_verified",
"timers_activated": false,
"notification_sent_by_helper": false,
"production_data_restored": false,
"started_at": "2026-10-31T17:17:13.743344+00:00",
"helm_revision": 6,
"verified_resource_count": 87,
"after_resources_sha256": "600c4998e54b8e11845849c7d8e4c6b6699556c21f5df6ca79bb86b2e8d1683f",
"live_desired_fields_verified": true,
"runtime_config_verified": true,
"existing_image_and_pvc_preserved": true,
"completed_at": "2026-10-31T17:17:31.488654+00:00"
},
"receipt_sha256": "c26d636f59109956f281ad46f4885c4db800a9553cf05cac324aa3155ef7ac40",
"original_execution_packet_matches": true,
"live_desired_fields_verified": true,
"runtime_config_verified": true,
"existing_image_and_pvc_preserved": true,
"runtime_modified_by_verification": false
}

View file

@ -3,7 +3,9 @@
The final wrapper is prepared for CSOC-WP-0012-T08. It installs the previously
sealed22-member archive into its exact19 target paths, using the existing
transaction library. It neither captures data nor starts/enables a service or
timer. Current native preflight **refuses**; do not execute this packet yet.
timer. The earlier custody refusal was resolved by verified T15 execution at
16:36:38Z. T07's route return and a fresh installer preflight remain required;
do not execute this packet yet.
Proposal:
`sha256:8f442bf0ebed8bbf3b7f227b52081df52613f3f5f6874c4a59511cfc2ba06d42`.
@ -105,7 +107,7 @@ corrected. The sender preparer's embedded remote Python AST is unchanged, and
the send-free return-mail rehearsal passed again. Historical evidence keeps its
original source hashes; the new validation binds the current formatted sources.
T08 remains progress until actual installation and independent inactive-state
T08 remains wait until actual installation and independent inactive-state
readback. T09 still needs native service/failure/retry/stale/producer-death proof;
T10 still needs activation and observation after its existing prerequisites.
No full service acceptance, mail delivery or migration verdict follows from

View file

@ -0,0 +1,131 @@
{
"schema": "coulomb.bubble-access-audit-result.v1",
"spaces": {
"records": 70,
"field_states": {
"Created By": {
"unresolved_reference": 5,
"observed": 64,
"missing": 1
},
"crew_list_user": {
"observed": 69,
"missing": 1
},
"members_list_user": {
"observed": 47,
"missing": 23
},
"guests_list_user": {
"missing": 69,
"observed": 1
}
},
"option_fields": {
"isprivate_boolean": {
"present": 69,
"missing": 1,
"distinct_observed_values": 2
},
"addnewusersasmember_boolean": {
"present": 20,
"missing": 50,
"distinct_observed_values": 2
},
"addnewusersasguest_boolean": {
"present": 21,
"missing": 49,
"distinct_observed_values": 2
}
}
},
"articles": {
"records": 245,
"field_states": {
"Created By": {
"observed": 239,
"unresolved_reference": 4,
"missing": 2
},
"_creator_user": {
"missing": 10,
"observed": 235
},
"_owner_user": {
"observed": 243,
"missing": 2
},
"moders_list_user": {
"missing": 71,
"observed": 174
}
},
"option_fields": {
"access_option_visibility": {
"present": 243,
"missing": 2,
"distinct_observed_values": 2
}
}
},
"known_spaces": 71,
"fresh_space_records": 70,
"inventory_only_spaces": 1,
"distinct_source_principals": 3,
"target_identity_mappings_established": 0,
"target_grants_proposed": 0,
"production_changed": false,
"source_visibility_semantics_verified": false,
"source_sha256": {
"spaces": "ae20b0cfd1705f486c0b67ef48ac84b396f953cbcef09f9a8f07d07a66f1bf71",
"articles": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"retained_spaces": "92038249ec2e4eed4c8d61ffcdbe030232cebc52e59ba005baee506a16930550"
},
"source_report_sha256": "75fcd87d17ea0c5042e903efd60e65839da7716e5399ff431a345486ddd10f01",
"recorded_at": "2026-10-31T14:05:30.738728+00:00",
"workplan_task": "CSOC-WP-0014-T01",
"task_status": "progress",
"accepted_mapping_scope_evidence": "docs/release/reichelag-imported-2026-10-06.md",
"validation": {
"focused_tests_passed": 13,
"full_gate": "make check passed",
"junit": {
"tests": 583,
"errors": 0,
"failures": 0,
"skipped": 0
},
"source_snapshots_unchanged": true,
"private_output_hashes_verified": true,
"output_modes_verified": true
},
"statehub_readback": {
"task_id": "0002882d-f156-5a5d-b524-d04793f4684d",
"status": "progress",
"access_audit_description_synced": true,
"fix_consistency_exit_code": 0,
"automation_errors": 0,
"assessment_failures": 0,
"warnings": 6
},
"output_files": [
{
"path": "access-inventory.json",
"sha256": "d4f97ccc55867361048325f7227721a50a59bcf70ea1af4dfa3db48e8e703cb2"
},
{
"path": "index.html",
"sha256": "6c175577f8a34c7bda78f86461d68d2c2167708970517f417bc89d1fde37436b"
}
],
"implementation_files": [
{
"path": "scripts/audit-bubble-access.py",
"sha256": "6590a19e54b77521e31455944b085f92e784e2b85737579cb14caaeb8538807b"
},
{
"path": "tests/test_bubble_access_audit.py",
"sha256": "392a799d62828c7508a1b25133d9c1c8de01959b7f1fec90ae737a7cefce7883"
}
]
}

View file

@ -0,0 +1,41 @@
{
"schema": "coulomb.bubble-attachment-recovery.v1",
"completed_at": "2026-10-31T19:35:10.719868+00:00",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"capture_sha256": "995fa2558bdfab1035e538152948843977e66e096b286cc762b45a232b0c0052",
"visited_pages": 6,
"previously_flagged_pages": 5,
"attachment_relations": 2,
"retained_files": 2,
"retained_bytes": 17529,
"all_ui_and_independent_download_bytes_match": true,
"original_five_parent_bodies_and_dates_unchanged": true,
"source_attachment_dates_retained": true,
"mapped_draft_pages": 1,
"example_parents_outside_original_capture": 1,
"new_attachment_child_records": 2,
"four_scrap_relations_resolved": false,
"original_bundle_files_preserved_in_place": 739,
"draft_body_and_existing_metadata_unchanged": true,
"source_resource_blockers_removed": false,
"resource_completeness_verified": false,
"production_imported": false,
"source_mutation_controls_activated": false,
"private_review_path": "/home/worsch/.config/coulomb-social/auth/exports/attachment-bundle-20261031/attachments-index.html",
"preparation_manifest_sha256": "f61f4dc48c4dcc66aa967df1bbf274f1027630a4417a82d249d04bb6b6b73184",
"validation": {
"full_gate": "TMPDIR=/var/tmp make check",
"passed": 670,
"errors": 0,
"failures": 0,
"skipped": 0,
"synthetic_resource_guards": 7,
"full_gate_log_sha256": "6195e8cd91bb2dca72e240159a51d4ced15c979f89f3909c9495b4b8874f6876"
},
"implementation_sha256": {
"scripts/capture-bubble-attachments.mjs": "cf8cba78d3a80f07f3448a50fb74d9502a01b84ff8be880c4d9e2a697af22812",
"scripts/bubble-resource-utils.mjs": "4f4e125f4d48ea5d8fb6421fb570d04a09fd2668e42c4c4857d4ac3a211e89b0",
"scripts/tests/bubble-resource-utils.test.mjs": "e434f56d9c586195f11ec1c642ef3f4da41d7cd2d4c01fc44f066bd3f76db192"
},
"private_preparation_driver_sha256": "e74d9571a6962e095bd299f15c47aa7a19f3223183f722a98a34a10450c29861"
}

View file

@ -0,0 +1,98 @@
{
"schema": "coulomb.bubble-body-resource-proof.v1",
"source_report_sha256": "75fcd87d17ea0c5042e903efd60e65839da7716e5399ff431a345486ddd10f01",
"visual_report_sha256": "a255df2237d95588b21a5db059487ca0178d1df28c810ebaaf744c5e6491267a",
"source_sha256": {
"articles": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"retained_spaces": "92038249ec2e4eed4c8d61ffcdbe030232cebc52e59ba005baee506a16930550",
"spaces": "ae20b0cfd1705f486c0b67ef48ac84b396f953cbcef09f9a8f07d07a66f1bf71"
},
"captured_articles": 245,
"source_bodies_verified": 242,
"missing_bodies": 3,
"prepared_pages_verified": 236,
"original_draft_files_recompiled_unchanged": 545,
"body_reference_inventory_entries": 18,
"body_reference_records": 1,
"body_reference_classifications": {
"external": 18
},
"reference_occurrences_before": 28,
"reference_occurrences_after": 28,
"records_with_changed_reference_attributes": 0,
"unresolved_scrap_relation_records": 4,
"attachment_flag_records": 1,
"live_destinations_checked": false,
"original_bubble_render_compared": false,
"productive_fidelity_accepted": false,
"production_imported": false,
"network_requests": 0,
"private_report_sha256": "4caec573982dafc82e7228d9221c941f585522e235d64d1f6edac3b43913cf26",
"recorded_at": "2026-10-31T14:11:42.653868+00:00",
"tasks": [
"CSOC-WP-0014-T01",
"CSOC-WP-0013-T02"
],
"task_statuses_changed": false,
"implementation_files": [
{
"path": "scripts/verify-bubble-body-resources.py",
"sha256": "8c9a56964ea59422830481d5aacb668e8589f6096ca5130fb6d541edc0ab4d8d"
},
{
"path": "scripts/verify-bubble-visual-preservation.py",
"sha256": "9fbc212bd668126a0f6ce6a458b1ca3fba1e14d4b44438c0697f63dc34b5bc3d"
},
{
"path": "coulomb_social/apps/spaces/rendering_audit.py",
"sha256": "093f293f6651a759d77ce7d444ba83fdfaead016b353a167693b2e36c5807894"
},
{
"path": "coulomb_social/apps/spaces/bubble_bundle.py",
"sha256": "fa928e049ba1052b736b13edf78883b249735312c6b98486e5dd16d4f2c96858"
},
{
"path": "tests/test_rendering_audit.py",
"sha256": "5e1aa72a322409a9576962c817d8714ee5eed042bbc38f22cb656a8319f52f67"
},
{
"path": "tests/test_bubble_bundle.py",
"sha256": "2ad0453be2c1c1643d4ef6af2e40dc270f70f863522aa221d1d4bb018a4f1f59"
},
{
"path": "tests/test_bubble_body_resources.py",
"sha256": "b3c420b62bceb253cd7228f3005b235c2d3bfd170136b960968311eb8fe48077"
}
],
"validation": {
"focused_tests_passed": 24,
"full_gate": "make check passed",
"current_verifier_reproduces_private_report": true,
"all_original_draft_bytes_verified": 545,
"junit": {
"tests": 595,
"errors": 0,
"failures": 0,
"skipped": 0
},
"focused_result_source": "full acceptance gate JUnit"
},
"statehub_readback": {
"tasks": [
{
"task_id": "0002882d-f156-5a5d-b524-d04793f4684d",
"status": "progress",
"body_resource_evidence_synced": true
},
{
"task_id": "8d06cdee-e0ce-5c3e-bf98-705deed0ee62",
"status": "progress",
"body_resource_evidence_synced": true
}
],
"fix_consistency_exit_code": 0,
"automation_errors": 0,
"assessment_failures": 0,
"warnings": 7
}
}

View file

@ -0,0 +1,102 @@
{
"schema": "coulomb.bubble-resource-observation-result.v1",
"recorded_at": "2026-10-31T14:35:31.435479+00:00",
"task": "CSOC-WP-0014-T01",
"status": "completed_bounded_observation",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"visited_articles": 5,
"route_matched_visits": 5,
"fresh_unchanged_parent_records": 5,
"typed_resources_observed": 0,
"empty_resource_envelopes": 0,
"response_status_counts": {
"200": 41
},
"response_timeouts": 0,
"response_read_failures": 2,
"retained_network_documents_checked": 294,
"retained_typed_resources_found": 0,
"linked_descriptor_targets_found": 0,
"settled_resource_controls_checked": 2,
"settled_control_label": "Add attachment",
"controls_activated": false,
"provider_queries_fabricated": false,
"resource_completeness_verified": false,
"resource_absence_inferred": false,
"operator_navigation_question_pending": true,
"original_capture_changed": false,
"production_imported": false,
"validation": {
"full_gate": "make check passed",
"junit": {
"tests": 596,
"errors": 0,
"failures": 0,
"skipped": 0
},
"synthetic_resource_node_tests": 4
},
"private_files": [
{
"path": "capture.json",
"sha256": "81a8ee56ee6885e5d54ebd184bf309effb01fade3ff5d7ff6229e9d96f973751"
},
{
"path": "index.html",
"sha256": "9218781bcf9273e2095494296d1d3f23d324e671c6d21920d69207b36181122f"
},
{
"path": "ui-resource-controls-settled.json",
"sha256": "ad0b3823db1d241ce1473df83bd2a7bba32da49e44dc48e0e1d5954bd8a6faa9"
},
{
"path": "ui-resource-controls.json",
"sha256": "4050f519bca5e4791ac0f0680a63eee2167c2b0e89c530ad045514bcb26a5da3"
},
{
"path": "ui-resource-observation.json",
"sha256": "704363f4bb6ea5dcc606da13b667b979dd419d631b14b2c8517deb90723ccd1e"
}
],
"implementation_files": [
{
"path": "scripts/capture-bubble-resources.mjs",
"sha256": "647691c4283ee09eb8b320bbf3ef943eb8ad6be8d365e98a6b28dfd2a43bc92a"
},
{
"path": "scripts/bubble-resource-utils.mjs",
"sha256": "e0cbd131bc28f67b3a88c45fb1e07cf12b33a71db051b5507e2e7ac14bf30170"
},
{
"path": "scripts/bubble-corpus-utils.mjs",
"sha256": "aeb867c5a4fc332e3c994a24e47c7f93cf9d1b8f95738871ef9971f81571f502"
},
{
"path": "scripts/tests/bubble-resource-utils.test.mjs",
"sha256": "d52dfc4fe8c3630ed020e9d6456e6836302c8f3e378bda424a1e3007873dee63"
},
{
"path": "tests/test_bubble_capture_guards.py",
"sha256": "384e7da7eee76ca7f55cfcb45f9e2d8722509c9d0f3668288053b56f410047e1"
}
],
"statehub_readback": {
"tasks": [
{
"task_id": "0002882d-f156-5a5d-b524-d04793f4684d",
"status": "progress",
"resource_evidence_synced": true
},
{
"task_id": "8d06cdee-e0ce-5c3e-bf98-705deed0ee62",
"status": "progress",
"resource_evidence_synced": true
}
],
"t01_intervention_note_matches_source": true,
"fix_consistency_exit_code": 0,
"automation_errors": 0,
"assessment_failures": 0,
"warnings": 7
}
}

View file

@ -0,0 +1,14 @@
{
"schema": "coulomb.bubble-resource-hover-observation.v1",
"completed_at": "2026-10-31T18:35:54.302241+00:00",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"private_observation_sha256": "79d9fcae0a894a04ffbba73d91b1080dcfa745fd0d9a9eb9c89e63908d00a412",
"articles_visited": 5,
"all_source_routes_matched": true,
"visible_images_hovered": 42,
"typed_resources_found": 0,
"visual_overlay_download_points_to_already_retained_visual": true,
"ui_controls_activated": false,
"resource_completeness_verified": false,
"production_changes": false
}

View file

@ -0,0 +1,22 @@
{
"schema": "coulomb.source-case-disposition.v1",
"status": "accepted_intentional_omission",
"source_field": "_list_custom_scrap",
"decision_id": "6db14274-4450-4ae2-b77a-cd4c88369eaa",
"decided_by": "Bernd",
"recorded_at": "2026-10-31T19:56:57.605591+00:00",
"user_instruction": "Lets ignore the scrap cases as they are legacy relicts that are no longer needed.",
"case_manifest_sha256": "c8c8394a7b801b7cf00ae3bb3411b0f12a52fd502b3f87ed0620124167d5a758",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"original_source_evidence_retained": true,
"page_bodies_or_dates_changed": false,
"production_data_deleted": false,
"other_source_exceptions_waived": false,
"case_numbers": [
1,
2,
3,
4
],
"private_disposition_sha256": "ae9aef7b6520734a22b4c276ba161ef6206872009a41ddc3d044007fe65d045d"
}

View file

@ -0,0 +1,14 @@
{
"schema": "coulomb.scrap-navigation-observation.v1",
"completed_at": "2026-10-31T19:51:07.317085+00:00",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"navigators_opened": 4,
"typed_resource_records": 0,
"absence_inferred": false,
"source_mutations": false,
"production_imported": false,
"private_observation_sha256": "e2eaf4a94c311107c5fd1a43371ca9bfd7a2cf5f2010f40097441bf1f67712c8",
"private_case_manifest_sha256": "c8c8394a7b801b7cf00ae3bb3411b0f12a52fd502b3f87ed0620124167d5a758",
"private_review_path": "/home/worsch/.config/coulomb-social/auth/exports/scrap-review-20261031/index.html",
"remaining_required_evidence": "Original scrap viewing path/content or explicit per-case disposition"
}

View file

@ -0,0 +1,110 @@
{
"schema": "coulomb.bubble-visual-preparation.v1",
"status": "captured_and_privately_prepared_not_imported",
"owner_task": "CSOC-WP-0014-T08",
"private_bundle": "~/.config/coulomb-social/auth/exports/visual-bundle-20261031",
"source_report_sha256": "75fcd87d17ea0c5042e903efd60e65839da7716e5399ff431a345486ddd10f01",
"capture": {
"references": 242,
"unique_urls": 108,
"downloaded_bytes": 168139156,
"materialized_pages": 236,
"body_rewriting_performed": false,
"grants_changed": false,
"browser_render_verified": false,
"production_imported": false
},
"browser": {
"schema": "coulomb.private-visual-decode.v1",
"status": "all_captured_visuals_decoded",
"decoded_images": 90,
"bundle_sha256": "a255df2237d95588b21a5db059487ca0178d1df28c810ebaaf744c5e6491267a",
"browser_network_disabled": true,
"productive_fidelity_accepted": false
},
"preservation": {
"schema": "coulomb.visual-preservation-proof.v1",
"verified_output_files": 738,
"verified_original_files": 545,
"frontmatter_only_changes": 303,
"all_source_bodies_and_dates_preserved": true,
"spaces_preserved": 71,
"original_blockers_preserved": true,
"unassigned_records_preserved": true,
"private_modes_verified": true,
"private_bundle_sha256": "a255df2237d95588b21a5db059487ca0178d1df28c810ebaaf744c5e6491267a",
"production_imported": false
},
"boundaries": {
"cdn_origins": 1,
"https_only": true,
"redirects_allowed": false,
"cookies_or_authorization_sent": false,
"live_application_or_grants_changed": false,
"source_completeness_or_fidelity_accepted": false,
"all_other_links_attachments_reconciled": false
},
"tests": {
"focused_passed": 24,
"full_gate_passed": 570,
"failures": 0,
"errors": 0,
"skipped": 0,
"warnings": 69,
"full_junit_sha256": "d11afa225571077d1d59c68711a5f0eb402de8ceff7f2f50c279fbba7f923349",
"final_verification_script": "native private bundle pass; final all-repo Ruff pass"
},
"remaining": {
"source_graph_exception_groups": 4,
"unassigned_article_records": 1,
"body_link_references": 18,
"attachment_review_flags": 1,
"identity_and_explicit_grants": "unresolved",
"productive_fidelity_verdict": "pending"
},
"sources": [
{
"path": "scripts/capture-bubble-visuals.py",
"sha256": "c16c8cf5013a6f02c9a9f79f444aea82ced7d8642b0fb2d2dc5315fabe92be7c"
},
{
"path": "scripts/verify-bubble-visuals.mjs",
"sha256": "2cb9625b0738028508957b8bc40a69b7ab2719046d20e0a69a0ac1a9394f56c5"
},
{
"path": "scripts/verify-bubble-visual-preservation.py",
"sha256": "9fbc212bd668126a0f6ce6a458b1ca3fba1e14d4b44438c0697f63dc34b5bc3d"
},
{
"path": "tests/test_bubble_visuals.py",
"sha256": "707fc9aa4ac0de5e40d33ee76571d74c141b361154a659f10c723a6597262006"
}
],
"captured_at": "2026-10-31T13:48:44.558963+00:00",
"goal_task_projection_readback": {
"workplans": [
{
"workplan": "CSOC-WP-0012",
"tasks": 14,
"done": 6
},
{
"workplan": "CSOC-WP-0013",
"tasks": 5,
"done": 2
},
{
"workplan": "CSOC-WP-0014",
"tasks": 8,
"done": 3
},
{
"workplan": "CSOC-WP-0015",
"tasks": 6,
"done": 3
}
],
"all_task_statuses_match_source": true,
"progress_event_id": "bb485164-d8ff-49cb-8c88-6aebe4e15cf0"
}
}

View file

@ -96,3 +96,19 @@ session revocation, process-log disposition and primary/mirror/protected-kit bac
expiry. The existing independent-kit no-expiry protection must not be silently
overridden. No client offboarding or productive deletion is authorized by this
implementation or its tests.
## October31 deployed return and offboarding scope
Revision19 has since deployed the reviewed exporter. Its packaged current-point
proof verifies85selected local files and four record sets, no selected external
bindings and unchanged source data; the temporary ZIP was removed afterward.
See `identity-export-release-client-export-2026-10-31.json`. The earlier source-only
and81file statements above retain their original dated scope.
The subsequent native scope check finds a second space outside the accepted
export tenant, with an external binding under the deployment default tenant.
Zero external bindings in the selected tenant must not be promoted to complete
instance attribution. The retained content PVC, five isolated database restores,
sessions, root journals and protected backup holds also need explicit disposition.
T12's concrete review is `client-offboarding-scope-review-2026-10-31.md`; it does
not authorize teardown or deletion of this active service.

View file

@ -0,0 +1,157 @@
{
"schema": "coulomb.client-offboarding-scope.v1",
"recorded_at": "2026-10-31T14:55:21.318848+00:00",
"task": "CSOC-WP-0012-T12",
"task_status": "progress",
"current_application_scope": {
"observed_at": "2026-10-31T14:51:29.215499+00:00",
"scope": "accepted tenant:platform only; canonical client binding unresolved",
"database_name": "coulomb_social_db",
"record_counts": {
"members.user": 1,
"members.member": 1,
"spaces.space": 1,
"spaces.spacemembership": 1
},
"selected_local_content_files": 85,
"external_binding_count": 0,
"all_spaces_count": 2,
"selected_spaces_count": 1,
"session_row_count": 43,
"export_ownership_checks_passed": true,
"read_only_repeatable_read_transaction": true,
"external_writer_fence": false,
"coherent_export_created": false,
"source_changed": false
},
"unselected_scope": {
"unselected_spaces": 1,
"unselected_external_bindings": 1,
"unselected_tenant_count": 1,
"unselected_tenants_match_deployment_default": true,
"unselected_spaces_with_creator": 0,
"canonical_client_assignment_inferred": false
},
"native_content_storage": {
"observed_at": "2026-10-31T14:49:42.586579+00:00",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"persistent_claims": [
{
"name": "coulomb-social-content",
"uid": "9888abeb-9d52-4c70-a23d-b5494780d9c3",
"phase": "Bound",
"storage_class": "local-path",
"capacity": {
"storage": "2Gi"
},
"helm_resource_policy": "keep",
"pv_name": "pvc-9888abeb-9d52-4c70-a23d-b5494780d9c3",
"pv_uid": "ac1aa73c-08d9-4561-995b-41060468e506",
"pv_reclaim_policy": "Delete",
"claim_uid_matches": true
}
],
"configuration_changed": false
},
"retained_local_restores": {
"containers": 5,
"running": 5,
"backing_database_volumes": 5,
"all_networks_internal": true,
"all_networks_single_container": true,
"published_ports": 0,
"public_receipt_linked_containers": 4,
"additional_private_receipt_sha256": "cc1d1be94da22756b52a00cc743ecd72b5cf8e956a7fd91288174dbb4499902b",
"additional_public_binding": "docs/release/identity-export-release-client-export-2026-10-31.json",
"all_five_have_receipt_bindings": true,
"stopped_or_deleted": false
},
"scope_limits": {
"canonical_client_assignment_resolved": false,
"unselected_external_content_resolved": false,
"fresh_coherent_export_created": false,
"client_delivery_or_acceptance": false,
"sessions_decoded_or_revoked": false,
"instance_or_data_deleted": false,
"backup_hold_released": false,
"provider_expiry_performed": false,
"disposable_offboarding_rehearsal_performed": false,
"offboarding_accepted": false
},
"private_inventory_files": [
{
"name": "accepted-point-link.json",
"sha256": "eb54af2fe49bf90a02680d68c8f69335e8a591042479faa0367dc9c925f6f097"
},
{
"name": "current-application-scope.json",
"sha256": "e24c3112c52e8ffd80a146c4dec8bd00e187ca0de14661644dd58206ebc535a6"
},
{
"name": "native-storage.json",
"sha256": "e8f4c32462ffb9869ceaf8991874d14858705ebe59978eb9fc2743daa0135efc"
},
{
"name": "restore-scope-linked.json",
"sha256": "8f2af25c9c80c69319bd6b61cf96094fe4054ed27fc1c63ae586962987971111"
},
{
"name": "restore-scope-summary.json",
"sha256": "6d24beb8739c34eaa6c34b9d5829b161ba5e863eea1376103598b7a2f04822a5"
},
{
"name": "retained-restores.json",
"sha256": "5b24822d8025c83011a286f097ca7f88125e42979864660ec5613e5b83a1323d"
},
{
"name": "unselected-scope.json",
"sha256": "33df8b171bfc31713118b216f1fb3b113c716d42560a00585eaee0fa91dcd56d"
}
],
"evidence_sources": [
{
"path": "docs/release/identity-export-release-client-export-2026-10-31.json",
"sha256": "d1b9d88b850a5aed19159bee867c4b0723a7a1df9e13c179d57a3de9e9cb67af"
},
{
"path": "docs/release/provider-kit-recovery-post-date-2026-10-31.json",
"sha256": "b7191972f4e4f7d0b74ae7bbdeb93aabdb79e2baf27ccb49108f6f30f417182c"
},
{
"path": "docs/release/generation-retention-post-date-2026-10-31.json",
"sha256": "10c78d8a69c02f28f9ad5f126c4daac89f6988516e3234ea67eaee6c0b5be6df"
},
{
"path": "coulomb_social/apps/members/client_export.py",
"sha256": "223a9ee42af6f17829aa263abcae595626e292b887de85820f48fdfa4a6363fa"
},
{
"path": "/home/worsch/railiance-apps/helm/coulomb-social-values.yaml",
"sha256": "63a45d1cfb395eff7cfdf21ed2100fb9562b7a2ab6b896afecbebfaabacae547"
},
{
"path": "/home/worsch/railiance-apps/charts/coulomb-social/templates/content-pvc.yaml",
"sha256": "5c4ddcaee2b3dcdf17da061b1a7a2cb1e0398d2f56f4ef31790cfaaf648b0fee"
},
{
"path": "/home/worsch/the-custodian/canon/standards/business-app-service-contract_v0.1.md",
"sha256": "83d2799d39f957d88aa1d71c3f04f7c2a0e168390249dc409ce149128d44e80c"
}
],
"statehub_readback": {
"task_id": "54af74b0-3212-537e-a02c-d2d8a091caf9",
"status": "progress",
"scope_review_synced": true,
"fix_consistency_exit_code": 0,
"automation_errors": 0,
"assessment_failures": 0,
"warnings": 8
},
"validation": {
"native_scope_assertions_passed": true,
"private_modes_and_hashes_verified": true,
"source_hashes_verified": true,
"json_round_trip_checked": true,
"application_code_changed": false
}
}

View file

@ -0,0 +1,81 @@
# Client offboarding scope review — 2026-10-31
CSOC-WP-0012-T12 now has a concrete, non-destructive scope review. It remains
**progress**, not accepted or executable. This review neither closes the active
service nor authorizes deletion, session revocation, expiry or credential changes.
The governing [service contract §6.2](/home/worsch/the-custodian/canon/standards/business-app-service-contract_v0.1.md)
requires support for complete client export and deletion: export, instance
teardown and backup expiry. Demonstrating that capability does not require
tearing down this active client now. T12 still needs its agreed scope, private
export delivery/acceptance and independently verified teardown/expiry procedure.
## Verified scope and unresolved attribution
The packaged revision19 export proof covers the accepted `tenant:platform`:
one User, Member, Space and SpaceMembership and85local files. Its coherent
07:13recovery point had no external bindings in that selected tenant. The temporary
export was removed after validation; this is not a delivered client archive.
A fresh read-only application transaction at14:51Z repeats the ownership checks
and finds the same four record counts and85selected local files, with no selected
external binding. It also finds **two spaces in the instance**, one outside the
selected tenant. A separate bounded read confirms that unselected space has an
external binding and belongs to the configured default tenant (`tenant:coulomb`),
with no creator link. These facts do not establish its legal client, whether it
is dispensable, or whether its remote content has been preserved. T11's canonical
client/app/instance mapping and the disposition of this unselected binding must
precede any proposal to tear down the whole database or instance.
The live read observed43session rows. Those rows were counted, not decoded or
attributed to the selected client. The read used a read-only transaction; app
writers were not fenced and no new coherent export was created. Do not combine
its counts with the older export hash to claim a newly verified deliverable.
## Stores and owner returns
| Store or boundary | Established evidence | Required offboarding return |
| --- | --- | --- |
| App SQL data, local principals, grants and admin history | Database `coulomb_social_db`; schema-derived inventory and selected-tenant export guards; one unselected externally bound space | Resolve client/tenant scope, produce and privately deliver a final coherent export, obtain acceptance, then review the database/role action through the deployment/database owners. Preserve the shared apps-pg cluster and other databases. |
| Local pages, assets, aliases and all revisions |85selected local files; deployed content PVC `coulomb-social-content`, UID9888abeb-9d52-4c70-a23d-b5494780d9c3, Bound2Gi/local-path | Export all assigned content, including deleted-page revisions. A future teardown must separately identify the claim/PV and verify its storage outcome. The PVC has `helm.sh/resource-policy: keep`; uninstalling the release alone is not content deletion. |
| App sessions and transient OIDC state |43current session rows; intentionally excluded from the client ZIP | Establish the approved app/tenant scope and revoke its sessions through a reviewed action. A count does not attribute anonymous/transient sessions; do not infer authority to revoke shared identity-provider sessions. |
| External content and identity links | One unselected externally bound space; OIDC/NetKingdom and user-engine are shared owners | Resolve the remote content and client assignment. Review only application-specific links/grants with their owners; do not delete shared human identities, provider clients or services based on local Member removal. |
| Node backup and release state | Existing paired archives, release capsules and date-operation journals; date journals can retain original index bytes outside CONTENT_ROOT | Include all assigned private bytes and journal/preimage copies in the exact retained/deleted set. T04's custody repair changes ownership/modes only and is not a disposition or expiry approval. |
| Local retained restores | Five running PostgreSQL recovery containers, five volume-backed stores; internal networks, one container each, no published ports | Preserve them pending disposition. The private inventory records container IDs, volume/mount identities, networks and links to existing restore receipts. Future cleanup must bind exact identities and verify both database volumes and corresponding plaintext content directories; stopping a container is not deletion. |
| Private source captures, review bundles and local archives | Source inventory spans71spaces and several source principals; private review/export copies are outside the live app | Classify ownership before deleting any directory. The whole source capture is not automatically the selected tenant's dataset. Preserve originals, accepted evidence and unresolved source records until explicit disposition. |
| Primary provider copies | Dedicated Coulomb application-archive prefix in shared `railiance-platform-pg-backup` | Provider owner must inventory exact versions, retain the final accepted export/recovery point as agreed, then execute admitted expiry. Do not delete the shared bucket or other applications' prefixes. |
| Mirrored generations and public receipts | Existing dated generation selection and exact-version readback; expiry remains separately admitted | Recompute the current exact keep/expiry set at execution. Rolling retention is not full client offboarding; deleting one mirror does not remove primary or protected copies. |
| Protected kits, proofs and separate recovery-key custody | Latest provider kit4166eb8b/version1793432303941465 is explicitly outside automatic expiry | Obtain explicit owner disposition for all client-bearing kit versions, proofs and associated recovery custody. Existing no-expiry protection remains in force; destroying a key alone is not a demonstrated complete offboarding procedure. |
| Logs, telemetry and notification ledgers | App/process logs and operator state are outside the ZIP; email-connect is a shared owner | Establish attribution and the permitted retention/expiry outcome with each owner. No log, mail-ledger or telemetry payload inventory or removal is claimed here. Preserve unrelated applications and required retained records. |
| Deployment, endpoint and credential projections | Revision19 and its declared resource/identity boundaries remain active | After accepted export and explicit teardown admission, remove only the bound instance resources/projections and verify routing, workloads, data stores and owner returns. Keep shared cluster, forge, identity, mail, telemetry and provider services. |
The PVC's native PV reclaim policy is `Delete`, and its claim UID matches the
bound PVC. That policy is a configuration fact, not proof of deletion: the claim
has not been removed, and no storage teardown has been exercised.
## Evidence and closure sequence
The public aggregate proof is `client-offboarding-scope-2026-10-31.json`. Exact
local restore volume/path metadata stays in the private
`~/.config/coulomb-social/auth/exports/offboarding-review-20261031/` inventory.
Four restore containers link directly to public recovery receipts; the fifth
links through the private receipt whose SHA matches the published revision19
client-export proof. All five remain running and untouched.
1. Bind the canonical client to the actual tenant/instance scope and resolve the
unselected external binding; do not substitute the deployment default tenant.
2. Agree the complete store list and retention/expiry outcomes, including the
protected-kit hold, shared-owner stores and retained private copies.
3. Prepare the exact teardown/expiry procedure and demonstrate it on a disposable
scoped instance and provider-owner test resources. Keep production identities,
data and held backups out of that destructive rehearsal.
4. Produce a fresh coherent export for private delivery and obtain the required
client acceptance. The existing rehearsal archive no longer exists.
5. If actual offboarding is later requested, bind the then-current identities,
object versions, writer fence, accepted export and approvals before execution;
retain independent completion/exception receipts for every included store.
This review does not choose retention durations, release holds, infer a client ID
or claim that steps3–5 have been performed. It supplies the missing concrete
scope for T12 while preserving T04 retention, T11 attribution, WP-0013 portability
and WP-0014 source/grant obligations.

View file

@ -0,0 +1,12 @@
{
"schema": "coulomb.content-release-supervisor-disposition.v1",
"proposal_digest": "sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3",
"decision_id": "d9b70cc5-e035-40ae-8d0d-020190581202",
"approved_by": "Bernd",
"recorded_at": "2026-10-31T20:16:08.885956+00:00",
"user_instruction": "Good, then lets do deployment the productive acceptance work now.",
"status": "deployment_scope_approved_awaiting_founder_execution",
"execution_performed": false,
"human_only_execution_lane_changed": false,
"productive_acceptance_inferred": false
}

View file

@ -0,0 +1,45 @@
{
"schema": "coulomb.content-acceptance-build.v1",
"completed_at": "2026-10-31T18:38:08.525103+00:00",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"build_context": "clean restricted git archive",
"source_full_gate_tests": 568,
"runtime_packages_and_python_unchanged": true,
"runtime": {
"python": "3.12.16",
"distributions": {
"h11": "0.16.0",
"httpcore": "1.0.9",
"psycopg": "3.3.4",
"joserfc": "1.7.4",
"anyio": "4.14.2",
"cryptography": "50.0.0",
"idna": "3.18",
"certifi": "2026.7.22",
"packaging": "26.3",
"python-decouple": "3.8",
"psycopg-binary": "3.3.4",
"sqlparse": "0.5.5",
"whitenoise": "6.12.0",
"cffi": "2.1.1",
"markdown": "3.10.3",
"webencodings": "0.5.1",
"pycparser": "3.0",
"bleach": "6.4.0",
"authlib": "1.7.2",
"django": "6.1",
"typing_extensions": "4.16.0",
"gunicorn": "26.0.0",
"asgiref": "3.12.1",
"httpx": "0.28.1",
"dj-database-url": "3.1.2"
}
},
"django_checks": {
"check": "System check identified no issues (0 silenced).",
"makemigrations": "No changes detected"
},
"new_database_migrations": false,
"production_deployed": false
}

View file

@ -0,0 +1,67 @@
{
"schema": "coulomb.packaged-content-acceptance-recovery.v1",
"status": "packaged_recovered_export_and_views_verified",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"restore_receipt_sha256": "9dacbd44544f93abf99354158cf610b4fb1f4e59611b22056b3086e0033231ab",
"retained_point_captured_at": "2026-10-31T20:21:01Z",
"candidate_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"previous_image_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"client_export": {
"status": "recovered_client_export_verified",
"schema": "coulomb.client-dataset-export.v1",
"content_files": 85,
"records": {
"members.user": 1,
"members.member": 1,
"spaces.space": 1,
"spaces.spacemembership": 1
},
"admin_events": 0,
"archive_sha256": "597f5929cae723e9f5d895afac6f5c96022049cb6bd938d4908d6aa56221f4ea",
"offboarding_complete": false,
"all_source_tables_compared": 13,
"all_source_files_compared": 86,
"archive_member_hashes_verified": true,
"selected_client_content_complete_locally": true,
"source_sql_and_content_unchanged": true,
"external_bindings_require_review": 0,
"sessions_and_credentials_excluded": true
},
"source_content_files_unchanged": 86,
"all_tables_unchanged": true,
"application_source_overlay": false,
"network_internal": true,
"published_ports": 0,
"production_access": false,
"production_restore": false,
"driver_sha256": "ce8c34a0ad00f9c4ac1ba6dd2043884aa07c90e216074cae070626aa04bf1639"
}

View file

@ -0,0 +1,37 @@
{
"schema": "coulomb.image-release-independent-recovery.v1",
"status": "fresh_point_independently_retrieved_and_restored",
"completed_at": "2026-10-31T20:27:35.058428+00:00",
"captured_at": "2026-10-31T20:21:01Z",
"primary_version": "1793478121590106",
"mirror_version": "1793478426025763",
"mirror_receipt_version": "1793478427159655",
"ciphertext_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"independent_reader_used": true,
"unpack": {
"status": "decrypted_pair_verified",
"version_id": "1793478121590106",
"encrypted_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"captured_at": "2026-10-31T20:21:01Z",
"database_sha256": "c5faa20ac0c443635393e04cf5b154710780d829d6c085edc098e2dd7525fc6a",
"content_sha256": "1bf31bafc19d8c4847f3793463189f13ce4b470ed96ccdc614b269acca6c12c0",
"application_restore_verified": false
},
"restore": {
"status": "restored_verified",
"database_tables": 13,
"database_rows": 115,
"database_rows_and_sequences_match": true,
"content_files": 86,
"content_hashes_match": true,
"network_internal": true,
"published_ports": 0,
"postgres_image_id": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
"elapsed_seconds": 4.42,
"content_was_empty": false
},
"elapsed_seconds": 5.517,
"production_restore": false,
"current_kit_replaced": false,
"driver_sha256": "a88a63c0e990cc4d8d9180d1907ec367f2e7f54c81d00e79f38ded4f30384341"
}

View file

@ -0,0 +1,41 @@
{
"schema": "coulomb.content-acceptance-independent-postflight.v1",
"status": "deployed_image_and_complete_native_state_verified",
"proposal_digest": "sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3",
"receipt_sha256": "074ec03d62d43f35bd6c276213a28de4a7b0161132379221bec0e402b5334233",
"helm_revision": 20,
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"packaged_source_files_verified": 79,
"sole_ready_content_writer": true,
"all_frozen_capsule_files_verified": 23,
"installed_helper_hashes_verified": true,
"snapshot_pin_refreshed": true,
"public_health_landing_static_verified": true,
"postflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"fresh_paired_point": {
"captured_at": "2026-10-31T20:21:01Z",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"version_id": "1793478121590106",
"encrypted_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"encrypted_bytes": 2181848,
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed"
},
"timers_activated": false,
"production_data_restored": false,
"productive_acceptance_inferred": false
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.guarded-image-release.v1",
"proposal_digest": "sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3",
"started_at": "2026-10-31T18:41:21.634253+00:00",
"status": "live_release_preflight_verified",
"production_data_restored": false,
"timers_activated": false,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"preflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"live_changes": false
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.guarded-image-release.v1",
"proposal_digest": "sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3",
"started_at": "2026-10-31T20:14:42.622703+00:00",
"status": "live_release_preflight_verified",
"production_data_restored": false,
"timers_activated": false,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"preflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"live_changes": false
}

View file

@ -0,0 +1,11 @@
{
"schema": "coulomb.packaged-source-provenance.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"packaged_file_count": 79,
"all_packaged_source_bytes_match": true,
"source_revision_label_verified": true,
"registry_digest_resolves": true,
"application_source_overlay": false,
"manifest_sha256": "2eca0618df5bdf35928869c3b848be36190065a4cca8f855bba372df6c516556"
}

View file

@ -0,0 +1,70 @@
{
"completed_at": "2026-10-31T20:22:30.428195+00:00",
"elapsed_seconds": 97.22,
"existing_global_constraint_preserved": true,
"fresh_paired_point": {
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"application": "coulomb-social",
"application_restore_verified": false,
"captured_at": "2026-10-31T20:21:01Z",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"completed_at": "2026-10-31T20:22:02.185627+00:00",
"content_sha256": "1bf31bafc19d8c4847f3793463189f13ce4b470ed96ccdc614b269acca6c12c0",
"database_sha256": "c5faa20ac0c443635393e04cf5b154710780d829d6c085edc098e2dd7525fc6a",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"encrypted_bytes": 2181848,
"encrypted_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"immutable_create": true,
"local_archive": "20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"pause_and_capture_seconds": 59.82,
"remote_retrieval_verified": true,
"schema": "platform.application-primary-archive.v1",
"status": "offsite_fetched_verified",
"version_id": "1793478121590106"
},
"helm_revision": 20,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"pid": 1461378,
"postflight": {
"actual_owner_pages_rendered": 40,
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"production_identity_or_grant_created": false,
"tenant_slug_constraint": true,
"unsaved_outsider_page_denials": 40
},
"preflight": {
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"business_tables": 12,
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"tenant_slug_constraint": true
},
"private_values_printed": false,
"production_data_restored": false,
"proposal_digest": "sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3",
"public_health_landing_static_verified": true,
"schema": "coulomb.guarded-image-release.v1",
"snapshot_pin_refreshed": true,
"started_at": "2026-10-31T20:20:53.065197+00:00",
"status": "image_candidate_deployed_verified",
"timers_activated": false
}

View file

@ -0,0 +1,17 @@
{
"schema": "coulomb.content-release-reconciliation.v1",
"helm_revision": 20,
"snapshot_server_dry_run": {
"status": "reviewed",
"server_dry_run": true,
"image_pinned": true,
"pvc_read_only": true,
"db_scope": "coulomb_social_db",
"replicas_restored_to": 1
},
"backup_timers_inactive_disabled": true,
"snapshot_source_sha256": "23f92c96b840df61f92858004241e860e0ddb32ed6fd7448294d3c8278906e46",
"owner_live_values_match_sealed_values": true,
"recovery_container_stopped": true,
"snapshot_restore_regression_tests_passed": 9
}

View file

@ -0,0 +1,67 @@
{
"schema": "coulomb.packaged-content-acceptance-recovery.v1",
"status": "packaged_recovered_export_and_views_verified",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"restore_receipt_sha256": "3d6fe0b5d7426c9fb39ba22f18770319e0c89ba1981c61db171199d3b4e68524",
"retained_point_captured_at": "2026-10-31T07:13:52Z",
"candidate_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"previous_image_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"client_export": {
"status": "recovered_client_export_verified",
"schema": "coulomb.client-dataset-export.v1",
"content_files": 85,
"records": {
"members.user": 1,
"members.member": 1,
"spaces.space": 1,
"spaces.spacemembership": 1
},
"admin_events": 0,
"archive_sha256": "eb982edcdb94c976ab3d4e9797e4c34eb72a3ee3886a37e64508744ac172a8a8",
"offboarding_complete": false,
"all_source_tables_compared": 13,
"all_source_files_compared": 86,
"archive_member_hashes_verified": true,
"selected_client_content_complete_locally": true,
"source_sql_and_content_unchanged": true,
"external_bindings_require_review": 0,
"sessions_and_credentials_excluded": true
},
"source_content_files_unchanged": 86,
"all_tables_unchanged": true,
"application_source_overlay": false,
"network_internal": true,
"published_ports": 0,
"production_access": false,
"production_restore": false,
"driver_sha256": "da85e5f7d5cf68f599a897a932d51399340bdd026a7cddf9603bef4aeb40909a"
}

View file

@ -0,0 +1,4 @@
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-mirror-content-point.py: bc4d04c3f8ee3ca089fa9b038e6ab617eaababcb7f0aec94339e7f4eeb70f13a
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-recover-content-point.py: a88a63c0e990cc4d8d9180d1907ec367f2e7f54c81d00e79f38ded4f30384341
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-verify-deployed-content.py: fb6079fb614a23eba0985c1d4a2e5b776e2d873f29c158cf6f38254d1415b387
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-verify-fresh-content-point.py: ce8c34a0ad00f9c4ac1ba6dd2043884aa07c90e216074cae070626aa04bf1639

View file

@ -0,0 +1,137 @@
# Editor attachment follow-up — deployed and independently verified
Founder execution completed at Helm revision21 on2026-10-31T23:32:38Z.
All97business rows/98files remain unchanged;43owner/43outsider checks pass.
The command below is historical: do not rerun it. Evidence:
editor-attachments-{receipt,independent-postflight}-2026-11-01.json.
Productive review found that revision20 preserves attachments but does not show
them in edit tabs. Source `31eaa581a65e454b6ef9979326c5bdfe10e13806` adds the
saved-file list/downloads to the editor, retains it after stale-edit refusal,
and opens management separately so unsaved text remains in its tab.
Main670, isolated568 and packaged110 checks pass, including the two-editor browser
journey. Candidate/previous images pass recovered views/export without changing
source data. Full native preflight passes against revision20/97rows/98files.
All prior93rows/86files are unchanged; review added2spaces/2memberships/12files.
Image: `sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e`.
Proposal: `sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a`.
This captures a fresh paired backup, performs the atomic image-only upgrade,
checks unchanged data and refreshes the backup image pin. Rollback preserves
writes and restores revision20's image/pin. No schema, timer, purge or mail change.
Save work in open editor tabs before running; reopen the editor after verified
success. The current source-bound launcher explicitly requires founder-terminal
execution (tools/coulomb-image-release.py:1–5 in railiance-apps).
```bash
python3 /home/worsch/railiance-apps/tools/coulomb-image-release.py \
--plan /home/worsch/railiance-apps/release/coulomb-editor-attachments-31eaa58.json \
--execute \
--expect-digest sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a
```
At the prompt:
```text
DEPLOY sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a
```
Return the JSON result for independent verification. The next productive review
must check both edit tabs, downloads and stale-edit text/attachment preservation.
Evidence: editor-attachments-{validation,provenance,tests,recovery,target,baseline-change,preflight}-2026-10-31.json.
---
# Combined content release for WP-0013
Deployed and independently verified at Helm revision20, completed
2026-10-31T20:22:30Z. The command below is historical: do not rerun it.
All93business rows/86files are unchanged;40owner views/40outsider denials pass.
See content-acceptance-independent-postflight-2026-10-31.json.
The candidate adds the requested attachment action menu, including author uploads
and removal, authenticated downloads, and independent attachments on copies and
moves. It also restores ordered-list starting numbers and fixes interrupted
copy/move asset recovery. Removal detaches the current association; earlier
revisions retain the file. Existing body links and visuals remain editable in the
page editor. The upload limit is16 MiB per file and100 associations per page.
Source: `1139a6d30eee4f75317b46bd45120a15d818455f` on
`csoc-wp0013-content-release`. Immutable image:
`sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5`.
The working repository passes670tests; the isolated release branch passes568;
the actual image passes110focused checks without application-source overlays.
Every packaged source file matches that commit. Runtime dependencies/Python and
database migrations are unchanged. Historical release packets remain unchanged.
Both the candidate and previous image render40owner pages and deny40outsiders on
the retained independently restored07:13point. The packaged client export verifies
85files while all13SQL tables/86source files remain unchanged. The temporary
recovery database was returned to its previously stopped state. This retained
point has92business rows; current production has93. Session/login-only drift was
previously reviewed and its fingerprints are unchanged. Execution creates and
reads back a fresh paired backup before Helm; independent recovery of that new
point is now verified: all13tables/115rows and86files restore exactly; the
deployed and previous packages pass40owner/40outsider checks, and client export
verifies85files without altering source data. See content-acceptance-fresh-
{recovery,client-export}-2026-10-31.json.
The route and inactive installation prerequisites are independently verified.
Full recurring backup/service acceptance remains WP-0012. This proposal does not
activate timers, purge retained data, restore production data or send email.
## Exact attended action
Plan: `/home/worsch/railiance-apps/release/coulomb-content-acceptance-1139a6d.json`.
Proposal: `sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3`.
Baseline: revision19/image522bcd7f,93business rows/86files. The controller checks
complete data/spec fingerprints, creates the paired recovery point, performs the
atomic image-only Helm upgrade, verifies live views/data and refreshes the
snapshot image pin. Rollback restores the prior image/pin while preserving writes.
An older image retains the new attachment metadata/files but lacks the new menu.
Full native preflight reports `live_release_preflight_verified` at this exact
digest (receipt: content-acceptance-preflight-2026-10-31.json). Resolved approval
decision: d9b70cc5-e035-40ae-8d0d-020190581202. Executed command:
```bash
python3 /home/worsch/railiance-apps/tools/coulomb-image-release.py \
--plan /home/worsch/railiance-apps/release/coulomb-content-acceptance-1139a6d.json \
--execute \
--expect-digest sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3
```
At its prompt:
```text
DEPLOY sha256:e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3
```
Founder-terminal execution is required by the source-bound launcher and the
standing supervised execution contract in
`the-custodian/docs/agent-autonomy-decision.md`: “The supervisor approves that
proposal or performs it through the privileged execution path.” Bernd separately
approved and executed this proposal; the execution gate is satisfied.
The action pauses writes during backup capture and the Recreate rollout. It adds
one encrypted primary backup object and temporary scratch within the admitted
capacity limits; provider cost is unpriced and no autopilot budget is claimed.
Check the durable receipt before any retry. Required final status:
`image_candidate_deployed_verified`. Receipt path:
`/var/lib/coulomb-social-backup/releases/e96752ecfa7adef2fd1d9da48902c8e63de124ccfd272fe20ba79b758b3ca4a3/receipt.json`.
## Productive review after verified deployment
Use the prepared UC-003–006 steps in
[productive-content-acceptance-session.md](../usecases/productive-content-acceptance-session.md).
On a disposable page, open Attachments, add a file, download it as an authorized
reader, duplicate/move the page, and remove the copied association. Verify that
the original remains independent and revoked/outsider access is denied. Record
attachment, authoring, copy/move, sharing and portability verdicts separately.
The three custom-topic rendering cases are accepted for now under decision
69ec9380. The two downloadable attachments are recovered. Bernd explicitly omitted the
four reviewed scrap cases as obsolete legacy data under resolved decision6db14274.
Other source-body/graph exceptions remain with source reconciliation.
No productive acceptance or WP-0013 completion is inferred from these tests.

View file

@ -0,0 +1,21 @@
{
"baseline_revision": 19,
"baseline_helm_manifest_sha256": "93cb277abd1aea0657be482e458dcfb268fe0bfbd45d90aa1faedcc7f5bce499",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"baseline_spec_sha256": "42e091e52d5dae3c1f986e4898c62057fa80846adfce049a75d8fbd4681180ea",
"candidate_spec_sha256": "8d79c64cff4bd2c62f03494e80847dc99b965ddabf35645da67e8d1020220798",
"old_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"expected_data": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"image_only_server_preview": true
}

View file

@ -0,0 +1,13 @@
{
"schema": "coulomb.packaged-content-acceptance-tests.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"source": "1139a6d30eee4f75317b46bd45120a15d818455f",
"tests": 110,
"failures": 0,
"errors": 0,
"skipped": 0,
"network": "none",
"read_only_root": true,
"application_source_overlay": false,
"synthetic_fixtures_only": true
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.packaged-content-lifecycle-tests.v1",
"source": "23ffe2cf4b214666754a2420558943438e54251a",
"image_id": "sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"tests": 29,
"failures": 4,
"expected_baseline_regressions": true,
"failed_nodes": [
"test_process_death_during_copy_or_move_is_recoverable[copy_page-during_first_asset]",
"test_process_death_during_copy_or_move_is_recoverable[copy_page-during_second_asset]",
"test_process_death_during_copy_or_move_is_recoverable[transfer_page-during_first_asset]",
"test_process_death_during_copy_or_move_is_recoverable[transfer_page-during_second_asset]"
],
"errors": 0,
"skipped": 0,
"network": "none",
"read_only_root": true,
"application_source_overlay": false,
"test_tooling_injected": true,
"application_modules_from_image_verified": true,
"production_access": false,
"junit_sha256": "edc52bd3a321d028d255b3d478020b3383d94747957ffe2985583b1f0741cb85",
"driver_sha256": "85b65114997c7e7c5323cb77a0043e495ec132506eef07dd394c6a6361380d36",
"test_source_sha256": "84a25a22a3cbda397e39c0508db8ea5aa90e6c467ccfa7da2b568d174d1ec449"
}

View file

@ -0,0 +1,4 @@
/home/worsch/coulomb-social/coulomb_social/apps/spaces/pageops.py: 055ef25b5efc75798cf17ffa68739de4d4099d2cab4d041e7c7eacdfaf43803e
/home/worsch/coulomb-social/tests/test_content_recovery.py: 84a25a22a3cbda397e39c0508db8ea5aa90e6c467ccfa7da2b568d174d1ec449
/home/worsch/coulomb-social/scripts/verify-packaged-source.py: 99d734f20331e8d699081d3d77e7a0d0b3bc676be69ce101377bc69eae6c70d5
/tmp/csoc-packaged-lifecycle-tests-20261031.py: 85b65114997c7e7c5323cb77a0043e495ec132506eef07dd394c6a6361380d36

View file

@ -0,0 +1,119 @@
{
"schema": "coulomb.content-lifecycle-candidate-build.v1",
"completed_at": "2026-10-31T11:58:26.697591+00:00",
"source": "f306dbfa2604e4815523667740be2925c333b84e",
"tag": "forgejo.coulomb.social/coulomb/coulomb-social:f306dbfa2604",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"registry_digest_independently_verified": true,
"source_revision_label_verified": true,
"build_context": "Clean git archive restricted to Dockerfile, dockerignore, lock, project, manage.py, app and entrypoint.",
"build_inputs": [
{
"path": "Dockerfile",
"sha256": "d0e197a18db8980eb3e51cca842d693e10ab0b4208627bba28691d10719d4a00"
},
{
"path": ".dockerignore",
"sha256": "901c8b95d95549d0e78c1c3d6d0538bf0252c8a77e9409129e0cadfa55c7c56c"
},
{
"path": "pyproject.toml",
"sha256": "5758c56fd1be89b973861ccc5ecbadb495e742ee13ebd7e19a221a0f119c54ee"
},
{
"path": "uv.lock",
"sha256": "e8ebb6fc4b5abd1ac80f1c9012f1983642d0528a8a213cfcc394261606fad887"
},
{
"path": "scripts/docker-entrypoint.sh",
"sha256": "70100256ca41ae6afe77078c810a8b6012472f0a741919f403f32088f98ab463"
}
],
"runtime": {
"previous": {
"python": "3.12.16",
"distributions": {
"h11": "0.16.0",
"httpcore": "1.0.9",
"psycopg": "3.3.4",
"joserfc": "1.7.4",
"anyio": "4.14.2",
"cryptography": "50.0.0",
"idna": "3.18",
"certifi": "2026.7.22",
"packaging": "26.3",
"python-decouple": "3.8",
"psycopg-binary": "3.3.4",
"sqlparse": "0.5.5",
"whitenoise": "6.12.0",
"cffi": "2.1.1",
"markdown": "3.10.3",
"webencodings": "0.5.1",
"pycparser": "3.0",
"bleach": "6.4.0",
"authlib": "1.7.2",
"django": "6.1",
"typing_extensions": "4.16.0",
"gunicorn": "26.0.0",
"asgiref": "3.12.1",
"httpx": "0.28.1",
"dj-database-url": "3.1.2"
}
},
"candidate": {
"python": "3.12.16",
"distributions": {
"h11": "0.16.0",
"httpcore": "1.0.9",
"psycopg": "3.3.4",
"joserfc": "1.7.4",
"anyio": "4.14.2",
"cryptography": "50.0.0",
"idna": "3.18",
"certifi": "2026.7.22",
"packaging": "26.3",
"python-decouple": "3.8",
"psycopg-binary": "3.3.4",
"sqlparse": "0.5.5",
"whitenoise": "6.12.0",
"cffi": "2.1.1",
"markdown": "3.10.3",
"webencodings": "0.5.1",
"pycparser": "3.0",
"bleach": "6.4.0",
"authlib": "1.7.2",
"django": "6.1",
"typing_extensions": "4.16.0",
"gunicorn": "26.0.0",
"asgiref": "3.12.1",
"httpx": "0.28.1",
"dj-database-url": "3.1.2"
}
},
"runtime_packages_equal": true,
"python_versions_equal": true,
"django_check": {
"returncode": 0,
"output": "System check identified no issues (0 silenced)."
},
"migration_drift": {
"returncode": 0,
"output": "No changes detected"
}
},
"source_delta_from_revision_19": [
"coulomb_social/apps/spaces/pageops.py"
],
"new_database_migrations": false,
"production_deployed": false,
"live_release_pins_modified": false,
"source_full_gate_tests": 546,
"packaged_lifecycle_tests": 29,
"previous_image_expected_lifecycle_failures": 4,
"owner_release_task": "RAPPS-WP-0015-T07",
"remaining": [
"Exact current-target release packet, paired rollback evidence and candidate/previous-image restored-store checks.",
"Attended image release and independent native postflight.",
"Productive lifecycle verdict; retention policy already accepted under c3a191f2-ce06-483f-82d7-ef8c54ea0d5e."
]
}

View file

@ -0,0 +1,20 @@
{
"schema": "coulomb.packaged-source-provenance.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"network": "none",
"read_only_root": true,
"entrypoint_bypassed": true,
"database_contact": false,
"production_changes": false,
"comparisons": [
{
"commit": "f306dbfa2604e4815523667740be2925c333b84e",
"packaged_file_count": 80,
"source_file_count": 80,
"different_files": 0,
"all_packaged_source_bytes_match": true
}
],
"actual_packaged_manifest_sha256": "b5c8a16b4b3d30cc9d8a7a9f3ed61bd8cb0e182589aceb5e4127fff27a8ae482",
"dependency_or_build_reproducibility_proved": false
}

View file

@ -0,0 +1,65 @@
{
"schema": "coulomb.packaged-content-lifecycle-recovery.v1",
"status": "packaged_recovered_client_export_and_views_verified",
"source": "f306dbfa2604e4815523667740be2925c333b84e",
"image_id": "sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"restore_receipt_sha256": "966d6640e42e2f4940c66d1cd5c8a802cd950de0305572f3c8b720ee3d81ae2b",
"client_export": {
"status": "recovered_client_export_verified",
"schema": "coulomb.client-dataset-export.v1",
"content_files": 85,
"records": {
"members.user": 1,
"members.member": 1,
"spaces.space": 1,
"spaces.spacemembership": 1
},
"admin_events": 0,
"archive_sha256": "e364a61570e0083e729a3f314eb707c57347200438bd98170c77b8f7fcc2b337",
"offboarding_complete": false,
"all_source_tables_compared": 13,
"all_source_files_compared": 86,
"archive_member_hashes_verified": true,
"selected_client_content_complete_locally": true,
"source_sql_and_content_unchanged": true,
"external_bindings_require_review": 0,
"sessions_and_credentials_excluded": true
},
"candidate_application": {
"status": "isolated_application_recovery_verified",
"recovered_spaces_with_content": 1,
"actual_pages_rendered_through_view": 40,
"restored_owner_access_verified": true,
"synthetic_viewer_reads": 1,
"synthetic_viewer_mutation_denials": 1,
"same_tenant_ungranted_page_denials": 40,
"fixture_transaction_rolled_back": true,
"production_access": false,
"external_identity_provider_used": false,
"real_browser_journey_verified": false
},
"previous_image_application": {
"status": "isolated_application_recovery_verified",
"recovered_spaces_with_content": 1,
"actual_pages_rendered_through_view": 40,
"restored_owner_access_verified": true,
"synthetic_viewer_reads": 1,
"synthetic_viewer_mutation_denials": 1,
"same_tenant_ungranted_page_denials": 40,
"fixture_transaction_rolled_back": true,
"production_access": false,
"external_identity_provider_used": false,
"real_browser_journey_verified": false
},
"source_content_files_unchanged": 86,
"all_tables_unchanged_after_both_view_checks": true,
"application_source_overlay": false,
"export_archive_removed": true,
"network_internal": true,
"published_ports": 0,
"production_access": false,
"production_restore": false,
"post_date_correction_data_claimed": true,
"retained_point_captured_at": "2026-10-31T07:13:52Z",
"driver_sha256": "92b66225e5f9bdf03d0a0999b613370c42341a2ceef468f1743fb4301884cb25"
}

View file

@ -0,0 +1,20 @@
{
"schema": "coulomb.packaged-content-lifecycle-tests.v1",
"source": "f306dbfa2604e4815523667740be2925c333b84e",
"image_id": "sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"tests": 29,
"failures": 0,
"expected_baseline_regressions": false,
"failed_nodes": [],
"errors": 0,
"skipped": 0,
"network": "none",
"read_only_root": true,
"application_source_overlay": false,
"test_tooling_injected": true,
"application_modules_from_image_verified": true,
"production_access": false,
"junit_sha256": "f2968ef1048ec9af68a187b967d558db0f6ee76e765a8106fb7fab284953511f",
"driver_sha256": "85b65114997c7e7c5323cb77a0043e495ec132506eef07dd394c6a6361380d36",
"test_source_sha256": "84a25a22a3cbda397e39c0508db8ea5aa90e6c467ccfa7da2b568d174d1ec449"
}

View file

@ -0,0 +1,3 @@
/tmp/csoc-content-release-observation-20261031.py: 1065625ee5b0736ae68a5230684f0effcb2c92cf0f814154ee86450b5a8fda4a
/tmp/csoc-content-row-drift-20261031.py: 49c8f0b8a92524205e2b190bbefec76d9cc38d966c4d20ffa7391e85ff17bf0e
/tmp/csoc-packaged-lifecycle-recovery-20261031.py: 92b66225e5f9bdf03d0a0999b613370c42341a2ceef468f1743fb4301884cb25

View file

@ -0,0 +1,7 @@
{
"proposal_digest": "sha256:c9c3231620721ef3ea9b2bf22fc6c62fe3ed8efb91386cf7ec96acc6959dae08",
"execution_started": false,
"status": "release_refused_or_failed",
"failure_type": "Refused",
"failure_reason": "inactive_backup_installation_required_before_image_release"
}

View file

@ -0,0 +1,79 @@
{
"schema": "coulomb.content-release-preparation.v1",
"prepared_at": "2026-10-31T12:16:52.085864+00:00",
"status": "prepared_waiting_for_inactive_backup_installation",
"proposal_digest": "sha256:c9c3231620721ef3ea9b2bf22fc6c62fe3ed8efb91386cf7ec96acc6959dae08",
"capsule_members": 20,
"owner_task": "RAPPS-WP-0015-T07",
"owner_task_id": "8f448934-bdfe-539f-8cab-a739ec752491",
"consumer_task": "CSOC-WP-0013-T05",
"baseline_revision": 19,
"current_business_rows": 93,
"candidate_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"prerequisite_installation_digest": "sha256:8f442bf0ebed8bbf3b7f227b52081df52613f3f5f6874c4a59511cfc2ba06d42",
"ordering": [
"historical custody repair",
"recurring alert route",
"inactive backup-source installation",
"content image preflight and supervised release",
"native recurring proof and schedule activation"
],
"controller_tests": {
"tests": 40,
"failures": 0,
"errors": 0,
"skipped": 0
},
"helm_lint": "passed",
"image_only_local_render_difference": true,
"candidate_image_only_server_preview_verified": true,
"native_full_preflight": "refused_expected_missing_installation",
"full_preflight_passed": false,
"historical_custody_packet_digest_unchanged": true,
"inactive_installer_packet_digest_unchanged": true,
"release_executed": false,
"fresh_backup_created_by_preparation": false,
"production_data_restored": false,
"timers_activated": false,
"new_resources_committed": 0,
"cost_risk_review": {
"additional_infrastructure_reservations": 0,
"new_backup_objects": 1,
"encrypted_file_limit_bytes": 1073741824,
"incremental_provider_cost_eur": null,
"autopilot_budget_claimed": false,
"risks": [
"Brief application pause for paired capture and Recreate image rollout.",
"Rollback restores previous image/helper without rolling back productive data.",
"Uncertain or changed baseline refuses; inspect durable receipt before retry."
]
},
"files": [
{
"repo": "railiance-apps",
"path": "tools/coulomb-image-release.py",
"sha256": "4cefb7b4ce7fb7eece1cd78ce3c1712a2b5cc2f9c81acfed06e3c6ac7c4abad3"
},
{
"repo": "railiance-apps",
"path": "tools/coulomb_image_release_node.py",
"sha256": "5547b3809f34178e48404cd1590f665729b062b4333f561e2da81b56d435dece"
},
{
"repo": "railiance-apps",
"path": "tests/test_coulomb_image_release.py",
"sha256": "dc9cd51bd0698b46c5e2a4f2f0fb8f89eceed722a92bd7b03f7278ed77a497d4"
},
{
"repo": "railiance-apps",
"path": "release/coulomb-content-lifecycle-f306dbf.json",
"sha256": "1bcd840aee2a93130838b7430de279c56dd7aecbce4244765a85be9d0a7130e2"
}
],
"native_no_attempt_readback": {
"release_journal_exists": false,
"release_receipt_exists": false
},
"sequencing_decision_id": "de5a4528-0864-4769-b9d0-2f658b0310c6",
"owner_controller_commit": "9c748846413c6db3f13ae3e55627fe3f6ad89194"
}

View file

@ -0,0 +1,90 @@
{
"schema": "coulomb.reviewed-image-release.v1",
"proposal_id": "CSOC-CONTENT-LIFECYCLE-20261031",
"scope": "ADMINISTER @ realm:kubernetes/railiance01; supervised founder execution only",
"operation": "image_only_existing_migration_0003",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"baseline_revision": 19,
"baseline_helm_manifest_sha256": "93cb277abd1aea0657be482e458dcfb268fe0bfbd45d90aa1faedcc7f5bce499",
"candidate_render_sha256": "1606725273089b83689850ad1c25d1834faa15e00c8cf8aa24e1053273af71bc",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"pvc_uid": "9888abeb-9d52-4c70-a23d-b5494780d9c3",
"baseline_spec_sha256": "42e091e52d5dae3c1f986e4898c62057fa80846adfce049a75d8fbd4681180ea",
"candidate_spec_sha256": "c65cb691bab7054ae25de58cdb75933d383621ec4e049d2daa1b1bf52dce0bba",
"old_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"candidate_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"product_source": "f306dbfa2604e4815523667740be2925c333b84e",
"expected_data": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"public_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"installed_helper_hashes": {
"/opt/coulomb-social-backup/paired-backup.py": "d161c742d1a6a182460a57d0a1719591ab58fc94006f55169ad3d74442a93630",
"/opt/coulomb-social-backup/snapshot-content-and-db.py": "dcc8dd2a3989f279597595f9260b31b24fcc9b6dc787f04c17ef0491c53d830d",
"/opt/coulomb-social-backup/mirror-paired-backup.py": "9ceb26d9083af016c4b787907e4dce7bdf437c782fb2d15eb28d9e334e6e431c",
"/opt/coulomb-social-backup/check-paired-backup-health.py": "2d40ec6e4c59ea7fe1d46065d197b9a87ca6811f023cc6f3e20e4184a3490566",
"/opt/coulomb-social-backup/backup_capacity.py": "cb699a79e67b129239b5aa5d6f3b0d4ffaa9773a8b073227ad17fdecb5b9d893",
"/opt/railiance-platform/scripts/application_recovery_identities.py": "20f56255ed6eeec4aafe33f142bc3ee03f623d4957a7b0cabb741d340f15f79d",
"/opt/railiance-platform/scripts/application_recovery_mirror.py": "d48ab95e89b9eeae36269d853ebe4c44c22e1f57d4e14224f03d0b9aeeb34178",
"/opt/railiance-platform/scripts/application_recovery_resources.py": "ab70ec341e738272730eb0daf76840d0bca807caeb3c4e4c2357f162fbed4498",
"/opt/railiance-platform/scripts/primary_application_archive.py": "a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d",
"/opt/coulomb-social-backup/backup_alerts.py": "07522437d4276b92f0a44149adc6c9e81bb18dbe59ce0f1f9e58a48347700d0e",
"/opt/coulomb-social-backup/notify-backup-alert.py": "0bd53bc5ad22d8bcb0326fb18a17737744b06416fe785dad077308e45f051d5a",
"/opt/coulomb-social-backup/observe-backup-alerts.py": "17caf4fa65283242f618b039ff1d018801c9890ea2a9178912cbdd4b50ee2664"
},
"clipboard_static_sha256": "347eabe0c8d93bf439df02b5a08a7ea71004600e6ff4e8a42cecbee52a9f0413",
"fresh_backup_before_helm": true,
"requires_dataset_equality_before_and_after": true,
"node_request_percent_limit": 90,
"helm_atomic_timeout_seconds": 300,
"rollback": "Restore Helm revision19/image522bcd7f and verified snapshot pin; preserve all post-capture data. No production data restore or schema downgrade.",
"approval_status": "prepared_not_admitted",
"recovery_rehearsal_scope": "Retained independently recovered 2026-10-31T07:13:52Z point; candidate and previous image preserve all 13 source tables and 86 files. Live observation now has93 business rows versus92 in that point; exact fresh paired backup/readback is required before Helm. Independent restore of the new point remains consumer follow-through. Read-only drift comparison confines differences to sessions and user/member login timestamps; all content and migration history match the retained point.",
"rehearsal_evidence_sha256": {
"content-lifecycle-candidate-build-2026-10-31.json": "83ad2ec41c85f8bb5087a250fdb91126ed92f12351cdc72b353c6f0e1a26452b",
"content-lifecycle-candidate-provenance-2026-10-31.json": "35ed909e0a2fa5c57cea1fcc4fbebb877eae559ca9dc753c3da00f84806d7b73",
"content-lifecycle-candidate-tests-2026-10-31.json": "27a75cfee6bbda41f377b73d6a6c7ecde0c54806c113f1be5223dd2f2cf39b3b",
"content-lifecycle-baseline-tests-2026-10-31.json": "de14e4e10337990ccec1c483f7d943bdc7e11b03aa813aff47d00a9b38a07b65",
"content-lifecycle-candidate-recovery-2026-10-31.json": "8d15172e5e330d01f153336e67f6bffb4363d571530c31ea05db872ca10807a9",
"content-lifecycle-validation-2026-10-31.json": "cbb093af470588ca6e8eb3ec5bc1ccee0be537ab37a46625b6ac267c17343d99",
"content-lifecycle-release-target-2026-10-31.json": "e9b50be3c8284c5a9ae96d75b800cbc4090580fa811c2ecd098d73ce8cbee74e",
"content-lifecycle-release-row-drift-2026-10-31.json": "b10f65bf55e900199ae6037767f32e8901aaccb6f7f3a86461f13dd02ec8efce"
},
"new_resources_committed": 0,
"autopilot_or_eur_budget_claimed": false,
"values_file": "release/content-lifecycle-values.yaml",
"snapshot_file": "release/content-lifecycle-snapshot.py",
"evidence_directory": "release/content-lifecycle-evidence",
"prerequisite_installation_digest": "sha256:8f442bf0ebed8bbf3b7f227b52081df52613f3f5f6874c4a59511cfc2ba06d42",
"requires_inactive_backup_units": true,
"files": {
"tools/coulomb-image-release.py": "4cefb7b4ce7fb7eece1cd78ce3c1712a2b5cc2f9c81acfed06e3c6ac7c4abad3",
"tools/coulomb_image_release_node.py": "5547b3809f34178e48404cd1590f665729b062b4333f561e2da81b56d435dece",
"tools/coulomb_image_probe.py": "7d241b288992fbbc961b42a1311f7d0938b0c899c0675b7db92653c5ace6014f",
"release/content-lifecycle-values.yaml": "daa302f96bddb889c7fe4aa95b740e9f5184d2ac463cd2403431b3e94db86528",
"release/content-lifecycle-snapshot.py": "c1508043f473d2762723acda04944a36f5346e7641fbe6fdf21a91acd3921a54",
"charts/coulomb-social/Chart.yaml": "663d6c23c57457a03bc376ebafb8107ee7f150e150f4d4fb6a3a949fb21006aa",
"charts/coulomb-social/templates/_helpers.tpl": "65379818d484959e7221d3f538b59ffd6b8b7d3c9493250e47520576aee1fc68",
"charts/coulomb-social/templates/content-pvc.yaml": "5c4ddcaee2b3dcdf17da061b1a7a2cb1e0398d2f56f4ef31790cfaaf648b0fee",
"charts/coulomb-social/templates/deployment.yaml": "f90b7517959ec8785f578f917fec222287931e4f8a27f6f9e8fc0a28385f7df8",
"charts/coulomb-social/templates/service.yaml": "78ce0ec6990c5ad57703c7a8d5d2c88c8eb9302d325cb2ba9d2df34e5f90ebdd",
"charts/coulomb-social/values.yaml": "af48d133f7fe45f0d762b40878f306ef48262f6cbbca61808ffac5dd9fc9d241",
"release/content-lifecycle-evidence/content-lifecycle-baseline-tests-2026-10-31.json": "de14e4e10337990ccec1c483f7d943bdc7e11b03aa813aff47d00a9b38a07b65",
"release/content-lifecycle-evidence/content-lifecycle-candidate-build-2026-10-31.json": "83ad2ec41c85f8bb5087a250fdb91126ed92f12351cdc72b353c6f0e1a26452b",
"release/content-lifecycle-evidence/content-lifecycle-candidate-provenance-2026-10-31.json": "35ed909e0a2fa5c57cea1fcc4fbebb877eae559ca9dc753c3da00f84806d7b73",
"release/content-lifecycle-evidence/content-lifecycle-candidate-recovery-2026-10-31.json": "8d15172e5e330d01f153336e67f6bffb4363d571530c31ea05db872ca10807a9",
"release/content-lifecycle-evidence/content-lifecycle-candidate-tests-2026-10-31.json": "27a75cfee6bbda41f377b73d6a6c7ecde0c54806c113f1be5223dd2f2cf39b3b",
"release/content-lifecycle-evidence/content-lifecycle-release-row-drift-2026-10-31.json": "b10f65bf55e900199ae6037767f32e8901aaccb6f7f3a86461f13dd02ec8efce",
"release/content-lifecycle-evidence/content-lifecycle-release-target-2026-10-31.json": "e9b50be3c8284c5a9ae96d75b800cbc4090580fa811c2ecd098d73ce8cbee74e",
"release/content-lifecycle-evidence/content-lifecycle-validation-2026-10-31.json": "cbb093af470588ca6e8eb3ec5bc1ccee0be537ab37a46625b6ac267c17343d99"
}
}

View file

@ -0,0 +1,61 @@
{
"changed_columns": {
"django_session": [
"session_key",
"session_data",
"expire_date"
],
"members_member": [
"updated_at",
"last_login_at"
],
"members_user": [
"last_login"
]
},
"file_changes": [],
"retained_fingerprint": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 92,
"business_rows_sha256": "6243955479f7861fcdcd79b62f7f1e9f9a27c05a17bd2c17b792ebc98b2fa7b6",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"schema": "coulomb.content-release-row-drift.v1",
"changed_tables": [
{
"table": "django_session",
"retained_rows": 42,
"current_rows": 43,
"retained_sha256": "c958e33ec782f9f2fd1b3cceba4a64da0038fde51050f25905ae42de91ca6dde",
"current_sha256": "594e246ef6297d9be9b6e8d9eda076a5b2ab36afb9263606b719f64f51293ba6"
},
{
"table": "members_member",
"retained_rows": 1,
"current_rows": 1,
"retained_sha256": "4d465d2b9d1e6311e505f861f2ed4b67763a545a965f9a7d0c586a3d07e3e9a3",
"current_sha256": "ab82b3f2c5e49f8634c598c9642894747f4b6fcff0f5230ac27f41939feb8640"
},
{
"table": "members_user",
"retained_rows": 1,
"current_rows": 1,
"retained_sha256": "81ccb3a40191e910a68c101896df518a588c00231728934c4c900577f8ae4f12",
"current_sha256": "0fc7a71b4b80a71fbedce527bbd2cae3858cf6a062e5714fc231be13ff987256"
}
],
"all_other_business_tables_equal": true,
"migration_history_equal": true,
"content_equal": true,
"retained_point": "2026-10-31T07:13:52Z",
"current_business_rows": 93,
"current_business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"private_row_values_printed": false,
"production_changes": false
}

View file

@ -0,0 +1,62 @@
{
"schema": "coulomb.content-release-target-observation.v1",
"observed_at": "2026-10-31T12:07:24.322343+00:00",
"cluster_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"pvc_uid": "9888abeb-9d52-4c70-a23d-b5494780d9c3",
"baseline_revision": 19,
"baseline_helm_manifest_sha256": "93cb277abd1aea0657be482e458dcfb268fe0bfbd45d90aa1faedcc7f5bce499",
"baseline_spec_sha256": "42e091e52d5dae3c1f986e4898c62057fa80846adfce049a75d8fbd4681180ea",
"candidate_spec_sha256": "c65cb691bab7054ae25de58cdb75933d383621ec4e049d2daa1b1bf52dce0bba",
"old_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"candidate_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:d3f2f5bb90f0ba79f4e1d8098d2eb06f8940b8789e5ecb851914bbab364597ec",
"sole_ready_pvc_writer": true,
"existing_unmanaged_metadata_preserved": true,
"candidate_image_only_server_preview_verified": true,
"data": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"matches_verified_revision19_data": false,
"installed_helper_sha256": {
"/opt/coulomb-social-backup/paired-backup.py": "43e119acb6a4933b4e144c349a091e074e1a91ab61dbe15a65c8264c1d47661a",
"/opt/coulomb-social-backup/snapshot-content-and-db.py": "dcc8dd2a3989f279597595f9260b31b24fcc9b6dc787f04c17ef0491c53d830d",
"/opt/railiance-platform/scripts/primary_application_archive.py": "a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d"
},
"timers": {
"coulomb-social-paired-backup.timer": {
"LoadState": "not-found",
"ActiveState": "inactive",
"UnitFileState": ""
},
"coulomb-social-backup-freshness.timer": {
"LoadState": "not-found",
"ActiveState": "inactive",
"UnitFileState": ""
}
},
"prerequisites": {
"custody": {
"exists": false
},
"installation": {
"exists": false
},
"route": {
"exists": false
}
},
"production_data_modified": false,
"release_executed": false
}

View file

@ -0,0 +1,90 @@
# Inspecting uncertain corpus imports
CSOC-WP-0014-T07 preparation. The receipt-backed importer now reserves the new
Space UUID in its durable `started` checkpoint before import writes. Its SQL
operation creates that exact UUID exclusively. A competing row at the slug or
reserved UUID refuses instead of being updated or receiving imported grants.
The committed return must still have the reserved identity before completion.
Ordinary imports without receipt mode retain their existing behavior.
This closes an identity gap in the earlier checkpoint implementation: a started
receipt previously named no intended SQL identity, and the underlying generic
importer could update an existing same-tenant row when its content tree was absent.
Legacy completed checkpoints remain usable. Legacy started checkpoints remain
uncertain and are reported as identity-unbound; no identity is invented for them.
## Read-only inspection
The new management command is:
```sh
python manage.py inspect_content_corpus /private/reviewed/manifest.json \
--receipt-dir /private/import-attempt
```
It requires the original manifest/source/mapping/destination binding and an
existing checkpoint and lock files. It does not create missing directories or
locks. It takes the existing importer/content locks without invoking content
recovery, then reads SQL in one transaction. PostgreSQL uses `REPEATABLE READ,
READ ONLY`. It compares destination and checkpoint bookends and rechecks source
hashes before reporting. These cooperating locks are **not** proof of an external
writer fence; the report explicitly records that limit.
Output contains unit indexes, classifications, counts and hashes, without source
bodies, identity names or SQL rows. It binds the checkpoint bytes and observed SQL,
grants and files. Classifications distinguish:
- A reserved identity whose current source-derived metadata, mapped grants and
complete source file hashes match.
- A reserved identity with differences, including later content, metadata, grant
or original-date changes.
- Foreign/replacement identities, files without SQL, or SQL without files.
- No SQL row or published tree, which does not establish absence of staging.
- Older started receipts without a reserved identity.
- Pending destinations and the continued presence of completed identities.
Original space dates are compared when supplied by the source index; absence is
reported. Insertion/update timestamps that cannot be derived from source are
fingerprinted, not fabricated. A matching result does not prove that no intervening
edit ever occurred. Synthesized indexes or omitted extra source files cannot be
called byte-exact by the whole-tree comparison. Completed units may have later
edits; their original receipt remains historical evidence, not an overwrite target.
Unattributed `.import-*` entries are counted, never assigned to an attempt or removed.
Every result explicitly says that no checkpoint was completed, no retry was
authorized and no data was restored. The inspector does not convert `started` to
`completed` or `pending`. The deployment owner must still reconcile the specific
uncertain state with its retained backup, writer fence and reviewed operation.
Do not remove the checkpoint or use replacement import to bypass uncertainty.
The subsequent [matching-commit procedure](corpus-import-reconciliation-2026-10-31.md)
adds a separate, explicit checkpoint operation for one exactly matching reserved
commit. Inspection itself remains read-only. Partial, changed, unbound and
unattributed-staging states still require owner reconciliation.
## Verification and release boundary
Fifteen new cases cover matching committed state without receipt, partial stores,
legacy/unrelated identities, later edits/grants, original dates, active locks,
missing paths, changing observations and occupied-slug refusal. The existing real
process-exit tests now inspect both the pre-write and committed-without-receipt
states while proving unchanged checkpoints. A row inserted after corpus preflight
is retained without imported grants or destination files.
Full `make check`: **629 passed, 69 existing warnings, zero failures/errors/skips
in 71.11s**, including the required browser journeys. Ruff and whitespace checks
pass. Log: `/tmp/csoc-import-inspection-check-20261031.log`.
`scripts/rehearse-import-inspection.py` also runs the corpus, receipt and inspection
tests against disposable PostgreSQL 16.14 on an internal Docker network with no
published ports. It passed 60 checks of actual inspection transactions' read-only
and repeatable-read modes. Current source and locked development dependencies are explicit
read-only overlays on the cached application image; this is not packaged-candidate
or production proof. Its containers, anonymous test volumes and network were
removed. Source-bound receipt:
`corpus-import-inspection-postgres-2026-10-31.json`.
No live cohort, grant, deployment, release pin or previously reviewed executor
changed. T07 remains in progress: source/identity/grant reconciliation, a concrete
owner procedure for uncertain imports, supervised executor integration, release,
fresh backup/fencing, native postflight and productive acceptance remain required.

View file

@ -0,0 +1,28 @@
{
"schema": "coulomb.corpus-inspection-rehearsal.v1",
"proof": {
"status": "passed",
"database_vendor": "postgresql",
"server_version": 160014,
"inspection_transaction_mode_checks": 60,
"source_overlay": true,
"locked_development_dependencies": true,
"production_data_used": false
},
"app_dependency_image": "sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"postgres_image": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
"internal_network": true,
"published_ports": false,
"disposable_resources_removed": true,
"source_sha256": {
"scripts/rehearse-import-inspection.py": "2f560e25f00b08cdbcf1a27440c02fc74c57d255429b0496e3f4e25b7eb90468",
"uv.lock": "e8ebb6fc4b5abd1ac80f1c9012f1983642d0528a8a213cfcc394261606fad887",
"coulomb_social/apps/spaces/import_tree.py": "f232bde0d1741668ff28a6cb24306b7f67d8bf9fa3838507a8db4f4e80361784",
"coulomb_social/apps/spaces/import_receipts.py": "670a5eb48613df47bf8fd0cf7d9306f7d2e992d46dff69680e6e7998f64f9cb2",
"coulomb_social/apps/spaces/import_inspection.py": "627f7c006f6aedf337cd146b1052028afe8342ce820283cdfb7963266d9e754d",
"coulomb_social/apps/spaces/management/commands/inspect_content_corpus.py": "b083e39aa766c8ba268788fae98507780604d40832e119db1cd9f7c10f1aa212",
"tests/test_import_corpus.py": "5a0670c408740de86ce601cd9d2a0a12cf0aaa5afda416867605180c568a1c5f",
"tests/test_import_receipts.py": "134e8901991dc493333b7543bbd6ff1f1e7b7ca3d5cf9d7849d7c45212e20aab",
"tests/test_import_inspection.py": "890e783eec30dd1b9e93ad7c3ab3173b57174a096938520865eaaf420bc605fb"
}
}

View file

@ -0,0 +1,79 @@
# Restartable corpus import checkpoints
WP-0014-T07 preparation, 2026-10-31. The existing corpus importer committed one
space at a time, but a whole-manifest retry failed when an earlier destination
already existed. Its ordinary-error rollback did not constitute crash recovery.
`import_content_corpus --receipt-dir <private-directory>` now supports durable
boundaries between new-space imports. Existing commands without this option retain
their behavior. Receipt mode refuses `--replace` and any unreceipted SQL row or
content destination, even when only one of those stores exists. The entire corpus
is preflighted before import writes. Dry-run does not create content or receipts.
## Receipt contract
- A checkpoint binds the exact manifest bytes/order, source tree hashes, resolved
source paths, explicit mapped member IDs/roles, database configuration identity
and content root. It neither discovers identities nor grants approval. Plan order
is preserved; the reconciled manifest must supply the agreed LIFO order.
- The receipt directory must be owned by the operator, private and separate from
source and served content. Symlinks, shared permissions and hardlinked receipt
files are refused. Directory mode is0700 and files0600. A nonblocking lock stops
simultaneous use of one receipt directory; the existing content lock serializes
cooperating content writers.
- Each space moves from pending to started before import writes. On success, the
helper verifies the mapped grants, fsyncs content files/directories, then writes
an atomic, fsynced completed checkpoint containing the stable Space ID, content,
metadata and grant hashes and page/asset counts. It refuses an enclosing database
transaction, so a completed receipt cannot precede an outer rollback.
The later inspection follow-through also reserves the new Space UUID before
writes and uses exclusive SQL creation; see the linked review below.
- A retry checks the binding and skips completed spaces with the same stable SQL
identity. Later body, title and grant edits remain intact. A deleted or replaced
identity requires reconciliation. Historical completion hashes describe the
original return; they do not claim the current edited space is unchanged.
- A started checkpoint always stops automatic retry, including after a lost commit
reply or failed receipt write. A crash between the filesystem and SQL operations
may leave partial content or staging directories. This change does not implement
automatic repair within a space. Do not delete the checkpoint, use `--replace`,
or label it completed merely to bypass that stop.
## Execution boundary
Only synthetic isolated fixtures were imported for this change. No deployment,
production import, grant or release-packet change occurred. The previously approved
single-cohort executor is unchanged and does not use this new option.
Before remaining-cohort execution, the deployment owner must integrate the helper
into the existing supervised path with immutable reviewed sources, explicit mappings,
external writer fencing, a fresh paired backup and native postflight. The cooperating
content lock is not an external writer fence, and a database-configuration hash is
not independent cluster identity proof. The owner must mount a durable private
receipt directory outside served content and include it in recovery custody.
If an attempt leaves a started checkpoint, keep the relevant writers fenced,
retain the checkpoint, staged trees and paired backup, and inspect the actual SQL
and files. Any reconciliation/restore needs its concrete reviewed procedure; the
helper supplies no generic automatic repair or permission to restore production.
Source fidelity, original-date verification, assets/links and productive transfer
verdicts remain the existing workplan requirements.
Read-only classification is now available through `inspect_content_corpus`.
It distinguishes exact reserved identities from partial, changed, foreign and
legacy identity-unbound outcomes without editing a checkpoint or authorizing a
retry. See [inspection review](corpus-import-inspection-2026-10-31.md).
## Verification
The new synthetic suite exercises real process termination before import, after
SQL commit but before a completed receipt, and after the first completed space.
The completed-boundary retry preserves a later local edit and stable Space ID while
importing the next space. Other checks cover changed source/manifest/destination,
mapping refusal, existing SQL/content, private-path constraints, deleted identities,
concurrent receipt use, enclosing SQL transactions and receipt-write failure.
Full `make check`: **614 passed**,69warnings,zero failures/errors/skips in71.32s,
including the required browser journeys and all18new checkpoint cases. Log:
`/tmp/csoc-import-receipts-check-20261031.log`. State Hub synchronization passed
with8warnings and0errors; T07 reads back as progress. Log:
`/tmp/csoc-import-receipts-consistency-20261031.log`.

View file

@ -0,0 +1,96 @@
# Completing a matching uncertain import checkpoint
CSOC-WP-0014-T07 now has an explicit checkpoint-completion operation for an
already committed import whose reserved identity and complete source-derived
state match. This is source preparation, not a production reconciliation or
admission of any cohort. The existing native executor and frozen release packets
are unchanged.
## Owner procedure
Before any native use, the deployment owner must identify the exact original
attempt, stop and independently verify application and external writers, retain
the original checkpoint and a coherent paired recovery point, and confirm the
source/tenant/identity/grant mapping was admitted. Neither of the commands below
verifies an external writer fence or provider backup. The current production
image does not contain these new commands; owner integration and release remain
required.
1. Inspect the original manifest and existing private receipt directory using
`inspect_content_corpus`. Keep the result privately with the operation evidence.
2. Review the selected zero-based unit index, reserved identity, source binding,
complete file/grant/metadata comparison and the original operation's outcome.
Only `reserved_destination_matches_source`, with no unattributed `.import-*`
entries, is eligible for the operation below. A match describes current state;
it is not proof that no intervening edit ever occurred.
3. Supply the exact inspection SHA-256 and explicit completion action:
```sh
python manage.py reconcile_content_import /private/reviewed/manifest.json \
--receipt-dir /private/import-attempt \
--unit-index 0 \
--expect-inspection <inspection_sha256> \
--complete-matching-commit
```
The command reacquires the existing receipt/content locks and a consistent
read-only SQL transaction, recomputes the whole inspection, and refuses a
changed digest. It fsyncs the matching destination, rechecks the selected unit,
source and checkpoint bytes, then atomically replaces only the checkpoint.
The completed unit retains its original reserved identity, ordinary importer
fingerprints/counts, prior started unit and full hash/count inspection evidence.
SQL rows, grants and content bytes are not modified.
4. Independently read the checkpoint and inspect again. A lost response or failure
may occur after replacement; do not infer failure to complete from the absence
of terminal output. The previous inspection cannot be replayed against an
already completed checkpoint. An interrupted temporary checkpoint file is
retained as evidence and is not automatically deleted or adopted.
5. If continuation is separately admitted, the existing receipted importer skips
the completed unit and preserves later edits and grants. Completion of this
checkpoint does not authorize importing the remaining corpus.
## Other states remain explicit owner work
| Observed state | Required disposition |
| --- | --- |
| Reserved identity differs from source | Preserve later edits/grants; reconcile against the original operation and paired point. This command never overwrites them. |
| SQL only, files only, or neither | Retain the uncertain checkpoint and inspect private staging, transaction outcome and the paired recovery point. No automatic reset to pending, retry, restore or deletion is implemented here. |
| Foreign identity or legacy unbound started receipt | Resolve ownership and identity from independent evidence. Do not adopt a row because its slug matches. |
| Unattributed staging entries | Establish their exact attempt/ownership and disposition separately. Matching published bytes do not authorize cleaning up those entries. |
| Pending or already completed unit | No reconciliation mutation is permitted. Completed identities remain historical boundaries and may have valid later edits. |
The helper uses cooperating file locks and PostgreSQL repeatable-read/read-only
transactions. These are not an external writer fence. The return explicitly
reports that limit, no SQL/content changes, no restore, and no authorization to
continue imports. The owner must verify native prerequisites before execution;
passing synthetic tests does not supply them.
## Validation
The focused suite covers exact completion and evidence retention, unchanged SQL/
content, preservation of later edits on resumed import, stale evidence, changed
source/mapping/checkpoint, partial or foreign stores, legacy receipts, unattributed
staging, invalid selection, sync failure and drift during fsync. Real child-process
exits immediately before and after checkpoint replacement leave either the prior
started checkpoint or the completed checkpoint with its reconciliation evidence.
Full `make check`: **648 passed, 69 existing warnings, zero failures/errors/skips
in 150.38s**, including required browser journeys, rerun after the interrupted
session. Nineteen new reconciliation
cases pass; the focused corpus checkpoint/inspection/reconciliation selection
passes all 52 cases. Log: `/tmp/csoc-import-reconciliation-check-20261031.log`.
The isolated PostgreSQL 16.14 rehearsal passes with 243 actual read-only and
repeatable-read transaction checks. It uses synthetic data, explicit source and
locked dependency overlays, an internal Docker network and no published ports.
Its disposable containers, volumes and network were removed. Source-bound receipt:
`corpus-import-reconciliation-postgres-2026-10-31.json`. This is not packaged
candidate or production proof.
After resuming, all 12 source hashes in that PostgreSQL receipt were independently
checked against the current files and matched. State Hub consistency passed and
the T07 description was independently read back with the reconciliation evidence.
T07 remains in progress for the other reconciliation states, supervised owner
integration, source/grant admission, native release/import/postflight and
productive verdicts.

View file

@ -0,0 +1,31 @@
{
"schema": "coulomb.corpus-inspection-rehearsal.v1",
"proof": {
"status": "passed",
"database_vendor": "postgresql",
"server_version": 160014,
"inspection_transaction_mode_checks": 243,
"source_overlay": true,
"locked_development_dependencies": true,
"production_data_used": false
},
"app_dependency_image": "sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"postgres_image": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
"internal_network": true,
"published_ports": false,
"disposable_resources_removed": true,
"source_sha256": {
"scripts/rehearse-import-inspection.py": "34a95a57df6b93132a45c70f95b56b8ee9ef5be8677efa6f27f48c41bdc46f17",
"uv.lock": "e8ebb6fc4b5abd1ac80f1c9012f1983642d0528a8a213cfcc394261606fad887",
"coulomb_social/apps/spaces/import_tree.py": "f232bde0d1741668ff28a6cb24306b7f67d8bf9fa3838507a8db4f4e80361784",
"coulomb_social/apps/spaces/import_receipts.py": "670a5eb48613df47bf8fd0cf7d9306f7d2e992d46dff69680e6e7998f64f9cb2",
"coulomb_social/apps/spaces/import_inspection.py": "908a62bd31e0a6cfab3cc94f45c671ff4f1ec403035434790a29adfc5275f5ab",
"coulomb_social/apps/spaces/import_reconciliation.py": "75abc50ec1c6e15a12534eb39edc5ad3fa4128c37a354d4cea2bf6c37f745771",
"coulomb_social/apps/spaces/management/commands/inspect_content_corpus.py": "b083e39aa766c8ba268788fae98507780604d40832e119db1cd9f7c10f1aa212",
"coulomb_social/apps/spaces/management/commands/reconcile_content_import.py": "2e25d94ab3a9659e38a3ea4c8b6c2e87c710216ab5834a248fccdd00da9fdbb9",
"tests/test_import_corpus.py": "5a0670c408740de86ce601cd9d2a0a12cf0aaa5afda416867605180c568a1c5f",
"tests/test_import_receipts.py": "134e8901991dc493333b7543bbd6ff1f1e7b7ca3d5cf9d7849d7c45212e20aab",
"tests/test_import_inspection.py": "890e783eec30dd1b9e93ad7c3ab3173b57174a096938520865eaaf420bc605fb",
"tests/test_import_reconciliation.py": "89d7161715a2c298c30c6ae38f8d9647688e2ea261d2913a8a9b8969202ac298"
}
}

View file

@ -0,0 +1,6 @@
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-verify-deployed-editor.py: f5ecee55accd7653be1cdf7402dd85185d6fd6305bf676f7ce00c63ec6259bd8
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-editor-target.py: 945ae15fbc328e537ff41b778b92bfcee73754983546149cbbbd2b1503dfc6d6
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-verify-editor-package.py: 2c877018ffd329ec3f3c9a345aba4ee04608d6f21ba5eb372bebad53352bd58e
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-verify-editor-recovery.py: 29c53d551eec2b4f4d60b047f123c727eef76ee7677c054bbfb0ab008355cff9
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-editor-baseline-diff.py: 1680f58e00e78dbd50b2a45e2a0d006acc94c073fe7cfac27436647a902d34bd
/home/worsch/.config/railiance-platform/coulomb-social-recovery/content-point-recovery-20261031/csoc-build-editor.py: 27a07652118d5d42cc99f258f6dc2580eb84cbf8b7591b88c08f92a3c3c1dfcf

View file

@ -0,0 +1,39 @@
{
"schema": "coulomb.acceptance-baseline-change.v1",
"retained_point": "2026-10-31T20:21:01Z",
"old_business_rows": 93,
"new_business_rows": 97,
"table_changes": {
"spaces_space": {
"old": 2,
"new": 4,
"removed": 0,
"added": 2
},
"spaces_spacemembership": {
"old": 1,
"new": 3,
"removed": 0,
"added": 2
}
},
"old_content_files": 86,
"new_content_files": 98,
"old_files_missing": 0,
"old_files_changed": 0,
"new_files_added": 12,
"old_rows_preserved": true,
"current_baseline": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"interpretation": "Changes observed during the user-requested productive acceptance session; no user verdict inferred."
}

View file

@ -0,0 +1,45 @@
{
"schema": "coulomb.content-acceptance-build.v1",
"completed_at": "2026-10-31T21:13:04.039838+00:00",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"build_context": "clean restricted git archive",
"source_full_gate_tests": 568,
"runtime_packages_and_python_unchanged": true,
"runtime": {
"python": "3.12.16",
"distributions": {
"h11": "0.16.0",
"httpcore": "1.0.9",
"psycopg": "3.3.4",
"joserfc": "1.7.4",
"anyio": "4.14.2",
"cryptography": "50.0.0",
"idna": "3.18",
"certifi": "2026.7.22",
"packaging": "26.3",
"python-decouple": "3.8",
"psycopg-binary": "3.3.4",
"sqlparse": "0.5.5",
"whitenoise": "6.12.0",
"cffi": "2.1.1",
"markdown": "3.10.3",
"webencodings": "0.5.1",
"pycparser": "3.0",
"bleach": "6.4.0",
"authlib": "1.7.2",
"django": "6.1",
"typing_extensions": "4.16.0",
"gunicorn": "26.0.0",
"asgiref": "3.12.1",
"httpx": "0.28.1",
"dj-database-url": "3.1.2"
}
},
"django_checks": {
"check": "System check identified no issues (0 silenced).",
"makemigrations": "No changes detected"
},
"new_database_migrations": false,
"production_deployed": false
}

View file

@ -0,0 +1,72 @@
{
"schema": "coulomb.packaged-content-acceptance-recovery.v1",
"status": "packaged_recovered_export_and_views_verified",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"restore_receipt_sha256": "1a696920d4cce738553ad536b62820e2bfda01cc89a7bd3d3eb8d5ceb3e266bb",
"retained_point_captured_at": "2026-10-31T23:29:54Z",
"candidate_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 43,
"unsaved_outsider_page_denials": 43,
"production_identity_or_grant_created": false
},
"previous_image_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 43,
"unsaved_outsider_page_denials": 43,
"production_identity_or_grant_created": false
},
"client_exports": [
{
"tenant_sha256": "98dd71602ef1afc56f1dcbaa7f5517198fcdd093a57d823ba11e8a634ea58ec2",
"status": "recovered_client_export_verified",
"schema": "coulomb.client-dataset-export.v1",
"content_files": 97,
"records": {
"members.user": 1,
"members.member": 1,
"spaces.space": 3,
"spaces.spacemembership": 3
},
"admin_events": 0,
"archive_sha256": "a4eb3daa1ff3427ff71eeaeb415b9ce645d84a65d81afbb0470d47478bc58b6a",
"offboarding_complete": false,
"all_source_tables_compared": 13,
"all_source_files_compared": 98,
"archive_member_hashes_verified": true,
"selected_client_content_complete_locally": true,
"source_sql_and_content_unchanged": true,
"external_bindings_require_review": 0,
"sessions_and_credentials_excluded": true
}
],
"exported_tenant_count": 1,
"source_content_files_unchanged": 98,
"all_tables_unchanged": true,
"application_source_overlay": false,
"network_internal": true,
"published_ports": 0,
"production_access": false,
"production_restore": false,
"driver_sha256": "5bfac786cbbf64bdbbd5b4b19d9fddfe0f99a28499417dbbbc0c2ca74e2cc69a",
"verification_query_note": "Deduplicate tenant IDs in memory: Space ordering by title/slug caused values_list(...).distinct() to repeat the same tenant across spaces. The corrected check exports each actual content tenant; this point has one."
}

View file

@ -0,0 +1,37 @@
{
"schema": "coulomb.image-release-independent-recovery.v1",
"status": "fresh_point_independently_retrieved_and_restored",
"completed_at": "2026-10-31T23:36:36.772911+00:00",
"captured_at": "2026-10-31T23:29:54Z",
"primary_version": "1793489481116168",
"mirror_version": "1793489725683064",
"mirror_receipt_version": "1793489727604085",
"ciphertext_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"independent_reader_used": true,
"unpack": {
"status": "decrypted_pair_verified",
"version_id": "1793489481116168",
"encrypted_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"captured_at": "2026-10-31T23:29:54Z",
"database_sha256": "4c49933e4fb81573257b7c64e95edd7f0395f20c9e6280348bb10383fd8eaa25",
"content_sha256": "0768e4f61ef003cff76c31aaca687e07e1743ff15a4d2d92dc0ebf123d4ec55e",
"application_restore_verified": false
},
"restore": {
"status": "restored_verified",
"database_tables": 13,
"database_rows": 119,
"database_rows_and_sequences_match": true,
"content_files": 98,
"content_hashes_match": true,
"network_internal": true,
"published_ports": 0,
"postgres_image_id": "sha256:ff23cdce56cac62ada6f66013e1a50864c0abbe79d132d40b6e05bd80f378a70",
"elapsed_seconds": 20.38,
"content_was_empty": false
},
"elapsed_seconds": 27.87,
"production_restore": false,
"current_kit_replaced": false,
"driver_sha256": "016bc4824ca885c765f8e7c267f1da8ad4b9c76ed47cc73c29c7168e6e2e1204"
}

View file

@ -0,0 +1,41 @@
{
"schema": "coulomb.content-acceptance-independent-postflight.v1",
"status": "deployed_image_and_complete_native_state_verified",
"proposal_digest": "sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a",
"receipt_sha256": "abf5910f3b68790356cb2b579038401ef7395e8f213fd11b0f89603fbc4ada32",
"helm_revision": 21,
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"packaged_source_files_verified": 79,
"sole_ready_content_writer": true,
"all_frozen_capsule_files_verified": 21,
"installed_helper_hashes_verified": true,
"snapshot_pin_refreshed": true,
"public_health_landing_static_verified": true,
"postflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 43,
"unsaved_outsider_page_denials": 43,
"production_identity_or_grant_created": false
},
"fresh_paired_point": {
"captured_at": "2026-10-31T23:29:54Z",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"version_id": "1793489481116168",
"encrypted_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"encrypted_bytes": 2181848,
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5"
},
"timers_activated": false,
"production_data_restored": false,
"productive_acceptance_inferred": false
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.live-editor-attachment-check.v1",
"status": "live_saved_attachments_visible_in_editors",
"helm_revision": 21,
"actual_user_verdict_inferred": false,
"production_writes": false,
"checks": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 43,
"live_editors_rendered": 43,
"editors_with_saved_attachments": 41,
"editor_download_links_verified": 42,
"unsaved_outsider_page_denials": 43,
"production_identity_or_grant_created": false
}
}

View file

@ -0,0 +1,25 @@
{
"schema": "coulomb.guarded-image-release.v1",
"proposal_digest": "sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a",
"started_at": "2026-10-31T21:15:08.583669+00:00",
"status": "live_release_preflight_verified",
"production_data_restored": false,
"timers_activated": false,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"preflight": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"live_changes": false
}

View file

@ -0,0 +1,11 @@
{
"schema": "coulomb.packaged-source-provenance.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"packaged_file_count": 79,
"all_packaged_source_bytes_match": true,
"source_revision_label_verified": true,
"registry_digest_resolves": true,
"application_source_overlay": false,
"manifest_sha256": "99401a60fdd9a95aff1168858e81fd7d8802cd7603f15c0ea35eb6917981f9fe"
}

View file

@ -0,0 +1,70 @@
{
"completed_at": "2026-10-31T23:32:38.446589+00:00",
"elapsed_seconds": 173.091,
"existing_global_constraint_preserved": true,
"fresh_paired_point": {
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"application": "coulomb-social",
"application_restore_verified": false,
"captured_at": "2026-10-31T23:29:54Z",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"completed_at": "2026-10-31T23:31:21.582167+00:00",
"content_sha256": "0768e4f61ef003cff76c31aaca687e07e1743ff15a4d2d92dc0ebf123d4ec55e",
"database_sha256": "4c49933e4fb81573257b7c64e95edd7f0395f20c9e6280348bb10383fd8eaa25",
"destination": "s3://railiance-platform-pg-backup/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"encrypted_bytes": 2181848,
"encrypted_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"immutable_create": true,
"local_archive": "20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"pause_and_capture_seconds": 85.7,
"remote_retrieval_verified": true,
"schema": "platform.application-primary-archive.v1",
"status": "offsite_fetched_verified",
"version_id": "1793489481116168"
},
"helm_revision": 21,
"node_request_percent": {
"cpu": 68,
"memory": 76
},
"pid": 2007089,
"postflight": {
"actual_owner_pages_rendered": 43,
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"business_tables": 12,
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"production_identity_or_grant_created": false,
"tenant_slug_constraint": true,
"unsaved_outsider_page_denials": 43
},
"preflight": {
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"business_tables": 12,
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"duplicate_slug_groups": 0,
"global_slug_constraint": true,
"migration_0003_applied": true,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"tenant_slug_constraint": true
},
"private_values_printed": false,
"production_data_restored": false,
"proposal_digest": "sha256:2cddcf38b7052287c10dce467a200a74cfa9699c666ae9ac538fc82917129e7a",
"public_health_landing_static_verified": true,
"schema": "coulomb.guarded-image-release.v1",
"snapshot_pin_refreshed": true,
"started_at": "2026-10-31T23:29:45.196471+00:00",
"status": "image_candidate_deployed_verified",
"timers_activated": false
}

View file

@ -0,0 +1,17 @@
{
"schema": "coulomb.editor-release-reconciliation.v1",
"helm_revision": 21,
"snapshot_server_dry_run": {
"status": "reviewed",
"server_dry_run": true,
"image_pinned": true,
"pvc_read_only": true,
"db_scope": "coulomb_social_db",
"replicas_restored_to": 1
},
"backup_timers_inactive_disabled": true,
"snapshot_source_sha256": "d680edba707394bc1f5857fa86676d21e6a82d1e1b3b7838dfb628938de7529a",
"owner_live_values_match_sealed_values": true,
"snapshot_restore_regression_tests_passed": 9,
"recovery_container_stopped": true
}

View file

@ -0,0 +1,67 @@
{
"schema": "coulomb.packaged-content-acceptance-recovery.v1",
"status": "packaged_recovered_export_and_views_verified",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"restore_receipt_sha256": "9dacbd44544f93abf99354158cf610b4fb1f4e59611b22056b3086e0033231ab",
"retained_point_captured_at": "2026-10-31T20:21:01Z",
"candidate_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"previous_image_application": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 93,
"business_rows_sha256": "7953b37b02b80d89ca6869dab6b2b0b996541de20ca0dd1b294a5f25572fb2ca",
"content_files": 86,
"content_files_sha256": "ed535246f4e8f46fcd7ce01f936127996aff873c6936b297f7272194b594aeda",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0,
"actual_owner_pages_rendered": 40,
"unsaved_outsider_page_denials": 40,
"production_identity_or_grant_created": false
},
"client_export": {
"status": "recovered_client_export_verified",
"schema": "coulomb.client-dataset-export.v1",
"content_files": 85,
"records": {
"members.user": 1,
"members.member": 1,
"spaces.space": 1,
"spaces.spacemembership": 1
},
"admin_events": 0,
"archive_sha256": "ecf5dba7ce891475cd5df15eb30205606f805850af7339ecd46e1479b45d6c5f",
"offboarding_complete": false,
"all_source_tables_compared": 13,
"all_source_files_compared": 86,
"archive_member_hashes_verified": true,
"selected_client_content_complete_locally": true,
"source_sql_and_content_unchanged": true,
"external_bindings_require_review": 0,
"sessions_and_credentials_excluded": true
},
"source_content_files_unchanged": 86,
"all_tables_unchanged": true,
"application_source_overlay": false,
"network_internal": true,
"published_ports": 0,
"production_access": false,
"production_restore": false,
"driver_sha256": "29c53d551eec2b4f4d60b047f123c727eef76ee7677c054bbfb0ab008355cff9"
}

View file

@ -0,0 +1,4 @@
/home/worsch/.config/railiance-platform/coulomb-social-recovery/editor-point-recovery-20261101/csoc-mirror-editor-point.py: 2cba828db15a11415136eb17586c116ba10d06b835b5f8291ae858af7ad2b448
/home/worsch/.config/railiance-platform/coulomb-social-recovery/editor-point-recovery-20261101/csoc-recover-editor-point.py: 016bc4824ca885c765f8e7c267f1da8ad4b9c76ed47cc73c29c7168e6e2e1204
/home/worsch/.config/railiance-platform/coulomb-social-recovery/editor-point-recovery-20261101/csoc-verify-editor-fresh-point.py: 5bfac786cbbf64bdbbd5b4b19d9fddfe0f99a28499417dbbbc0c2ca74e2cc69a
/home/worsch/.config/railiance-platform/coulomb-social-recovery/editor-point-recovery-20261101/csoc-live-editor-check.py: 679336ef9d952e24f5138532cb9cdba84e5635c9a35a54ee7c80312e249d73b7

View file

@ -0,0 +1,21 @@
{
"baseline_revision": 20,
"baseline_helm_manifest_sha256": "e388f62c97e97d54d87671825bd0b05c854a70b0cca8a00faee452ae7841799e",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"baseline_spec_sha256": "8d79c64cff4bd2c62f03494e80847dc99b965ddabf35645da67e8d1020220798",
"candidate_spec_sha256": "4deab26fc15d63e09922259b3362cd4e5c6bfa10884de058f776c0de8b642b00",
"old_image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"expected_data": {
"business_tables": 12,
"migration_rows_sha256": "43471516387c0e2a3d35971033ed6af330e2963efe4da93293d97633825d04af",
"business_rows": 97,
"business_rows_sha256": "35b0ae7d9dd1ff21c2ceaa04649162545ec36a2c68629548238352d9cd0fb303",
"content_files": 98,
"content_files_sha256": "b657c0e470399b6c7500da500cbbec061bea12ab9633bd74e2f142d797b2d26a",
"global_slug_constraint": true,
"tenant_slug_constraint": true,
"migration_0003_applied": true,
"duplicate_slug_groups": 0
},
"image_only_server_preview": true
}

View file

@ -0,0 +1,13 @@
{
"schema": "coulomb.packaged-content-acceptance-tests.v1",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"tests": 110,
"failures": 0,
"errors": 0,
"skipped": 0,
"network": "none",
"read_only_root": true,
"application_source_overlay": false,
"synthetic_fixtures_only": true
}

View file

@ -0,0 +1,11 @@
{
"schema": "coulomb.productive-editor-attachment-return.v1",
"helm_revision": 21,
"source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"user": "Bernd",
"statement": "Both tabs now show the attachment",
"status": "reported_editor_visibility_gap_accepted_resolved",
"scope": "Attachment visibility in both reopened editor tabs",
"copy_move_sharing_portability_acceptance_inferred": false,
"complete_attachment_workflow_acceptance_inferred": false
}

View file

@ -0,0 +1,34 @@
{
"schema": "coulomb.editor-attachments-validation.v1",
"reported_gap": "Attachments visible on the original page were absent from editor tabs.",
"main_source": "8b38afddf3f3648f209c60b1bafe3dd25501d857",
"release_source": "31eaa581a65e454b6ef9979326c5bdfe10e13806",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:b58d858abd5b4acbd6b893c3f622f7a9fdb5e606164ab9866eb3cfaa04df1a0e",
"main_full_gate": {
"tests": 670,
"failures": 0,
"errors": 0,
"skipped": 0
},
"release_full_gate": {
"tests": 568,
"failures": 0,
"errors": 0,
"skipped": 0
},
"packaged_tests": 110,
"browser_verifies": [
"saved file list and exact download bytes in both editor tabs",
"management opens separately and keeps unsaved text",
"stale refusal keeps submitted text and attachments",
"save keeps attachments and rejects stale overwrite",
"existing copy/move/download/detach permissions journey"
],
"runtime_changes": [
"coulomb_social/apps/spaces/views.py",
"coulomb_social/templates/spaces/page_form.html"
],
"storage_schema_or_authority_changed": false,
"productive_acceptance_inferred": false,
"test_environment": "Main uses /var/tmp; isolated branch native observer requires /tmp. Final complete gates pass without deselection."
}

View file

@ -0,0 +1,200 @@
{
"schema": "coulomb.service-measurement-gap.v1",
"recorded_at": "2026-10-31T14:43:33.505165+00:00",
"task": "CSOC-WP-0012-T11",
"current_signals": {
"schema": "coulomb.existing-telemetry-observation.v1",
"observed_at": "2026-10-31T14:39:40.395913+00:00",
"service_name": "telemetry-prometheus",
"service_uid": "529df0e7-b8a6-4478-8f51-672f50e9eaae",
"selected_metric_names": [],
"queries": {
"coulomb_ready": "kube_deployment_status_replicas_available{namespace=\"coulomb-social\"}",
"coulomb_ingress_metadata": "kube_ingress_info{namespace=\"coulomb-social\"}",
"coulomb_scrape_targets": "up{namespace=\"coulomb-social\"}",
"probe_series_count": "count(probe_success)",
"traefik_series_count": "count(traefik_service_requests_total)"
},
"results": {
"coulomb_ready": {
"resultType": "vector",
"result": [
{
"metric": {
"__name__": "kube_deployment_status_replicas_available",
"container": "kube-state-metrics",
"deployment": "coulomb-social",
"endpoint": "http",
"instance": "10.42.0.7:8080",
"job": "kube-state-metrics",
"namespace": "coulomb-social",
"pod": "telemetry-kube-state-metrics-5c9cd96d7b-c7ch5",
"service": "telemetry-kube-state-metrics"
},
"value": [
1793457579.499,
"1"
]
}
]
},
"coulomb_ingress_metadata": {
"resultType": "vector",
"result": [
{
"metric": {
"__name__": "kube_ingress_info",
"container": "kube-state-metrics",
"endpoint": "http",
"ingress": "coulomb-social",
"ingressclass": "traefik",
"instance": "10.42.0.7:8080",
"job": "kube-state-metrics",
"namespace": "coulomb-social",
"pod": "telemetry-kube-state-metrics-5c9cd96d7b-c7ch5",
"service": "telemetry-kube-state-metrics"
},
"value": [
1793457579.742,
"1"
]
}
]
},
"coulomb_scrape_targets": {
"resultType": "vector",
"result": []
},
"probe_series_count": {
"resultType": "vector",
"result": []
},
"traefik_series_count": {
"resultType": "vector",
"result": []
}
},
"runtime_modified": false
},
"retention_and_readiness_window": {
"observed_at": "2026-10-31T14:41:38.172491+00:00",
"retention_flags": {
"storage.tsdb.retention.size": "10GiB",
"storage.tsdb.retention.time": "10d"
},
"queries": {
"ready_samples_7d": "sum(count_over_time(kube_deployment_status_replicas_available{namespace=\"coulomb-social\",deployment=\"coulomb-social\"}[7d]))",
"minimum_available_replicas_7d": "min(min_over_time(kube_deployment_status_replicas_available{namespace=\"coulomb-social\",deployment=\"coulomb-social\"}[7d]))",
"maximum_available_replicas_7d": "max(max_over_time(kube_deployment_status_replicas_available{namespace=\"coulomb-social\",deployment=\"coulomb-social\"}[7d]))"
},
"results": {
"ready_samples_7d": {
"resultType": "vector",
"result": [
{
"metric": {},
"value": [
1793457697.673,
"20160"
]
}
]
},
"minimum_available_replicas_7d": {
"resultType": "vector",
"result": [
{
"metric": {},
"value": [
1793457697.955,
"0"
]
}
]
},
"maximum_available_replicas_7d": {
"resultType": "vector",
"result": [
{
"metric": {},
"value": [
1793457698.156,
"1"
]
}
]
}
},
"end_to_end_slo_proven": false,
"runtime_modified": false
},
"pending_execution_receipts": {
"observed_at": "2026-10-31T14:38:31.143707+00:00",
"receipts": {
"custody": {
"exists": false
},
"route": {
"exists": false
},
"installation": {
"exists": false
},
"content_release": {
"exists": false
}
},
"mutations": false
},
"interpretation": {
"application_slo_attainment_proven": false,
"monthly_application_window_proven": false,
"readiness_equals_http_availability": false,
"sample_count_proves_gapless_coverage": false,
"maintenance_or_dependency_exclusions_applied": false,
"application_latency_percentiles_available": false,
"configuration_changed": false,
"requires_application_level_signals_and_window_evidence": true
},
"sources": [
{
"path": "registry/release-units/coulomb-social.yaml",
"sha256": "309102bdee9d0ec0057c5d679be729ebd7aa3477d588962c2e757a38946761f6"
},
{
"path": "docs/resource-evidence/service-objectives-and-observations-2026-08.md",
"sha256": "f989738e569022cab572b676a64bc58565b2fad9b9476ea0a0d3c799e7c8d4e4"
},
{
"path": "docs/release/identity-export-release-return-2026-10-31.md",
"sha256": "e78222c650ff3939bb8e8f550e9b75534c9f43badea612a4ea3a2792fba82d01"
}
],
"current_release": {
"observed_at": "2026-10-31T14:44:54.910350+00:00",
"deployment_uid": "2dbe661a-d232-4477-bbd1-833813077fbe",
"generation": 49,
"observed_generation": 49,
"ready_replicas": 1,
"images": [
"forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed"
],
"helm_revision": 19,
"helm_status": "deployed"
},
"statehub_readback": {
"task_id": "61f5f196-40e4-5901-a10e-318304809a00",
"status": "progress",
"measurement_gap_synced": true,
"fix_consistency_exit_code": 0,
"automation_errors": 0,
"assessment_failures": 0,
"warnings": 6
},
"validation": {
"native_read_results_checked": true,
"json_round_trip_checked": true,
"application_code_changed": false,
"new_regression_run_required": false
}
}

View file

@ -139,3 +139,59 @@ is complete and waits on T04 historical custody/capacity and T07. T04 remains in
progress for those native returns and historical-copy disposition. The original
roughly 40% remaining estimate is unchanged: these source fixes do not establish
recurring operation, productive acceptance or full remaining-cohort transfer.
## Service provenance checkpoint
WP-0012-T14 now closes the Forgejo successor and existing founder identity-choice
bindings formerly inside T11. The records include resolved decisions, completed
owner migration and fresh read-only native metadata. T11 advances to progress and
retains actual ops registration, client/app/instance finance attribution/report,
current SLO and stage-1 smoke. This makes the source checklist13of32done; it is an
additional independently verified requirement, not a new product scope or an
updated effort estimate. The rough remaining-work estimate is unchanged.
## Visual preservation checkpoint
WP-0014-T08 completes the previously missing private Visual capture and draft-tree
materialization:242references,108URLs,90distinct images,236article pages and67
space indexes. Offline decoding and independent body/date/source preservation
checks pass. This advances the source checklist to14of33done, not an effort
percentage. Source exceptions, identity/grant mapping, links/attachments and
productive transfer/verdicts remain open; the rough remaining-effort estimate
has not been recalculated from these counts.
## Access reconciliation checkpoint
WP-0014-T01 now has a private, source-hash-bound access inventory covering the
70 fresh spaces and 245 articles, while retaining the inventory-only 71st space.
It identifies three distinct source principals and nine unresolved creator
references, preserving missing access fields as unknown. Existing ReichelAG
admission does not establish all-space identity or role mappings. The task remains
progress: this closes an evidence gap inside the task, not productive permission
reconciliation. The checklist stays 14 of 33 done, and the remaining-effort
estimate is unchanged. Evidence: `bubble-access-audit-2026-10-31.json`.
## Offboarding scope checkpoint
WP-0012-T12 moves from todo to progress with a concrete non-destructive scope
review. Fresh native evidence distinguishes the accepted85file tenant export from
the second, externally bound space; confirms the Helm-retained content PVC; and
accounts for five still-running isolated recovery databases and their receipts.
The review includes sessions, root journals, private copies and protected backup
holds. No teardown, deletion, revocation or expiry occurred. Canonical client
scope, agreed retention, procedure proof and private delivery/acceptance remain
open. The checklist stays14of33done; the effort estimate is unchanged. Evidence:
`client-offboarding-scope-2026-10-31.json` and its human-readable scope review.
## Corpus restart checkpoint
WP-0014-T07 moves from todo to progress with opt-in durable per-space import
receipts. Completed boundaries can be resumed without replaying completed spaces;
uncertain in-space outcomes require reconciliation. Synthetic process-exit tests
exercise both conditions. No remaining production cohort was imported. Source and
identity/grant reconciliation, supervised executor integration and productive
postflight/verdicts remain open. The checklist stays14of33done and the rough
remaining-effort estimate is unchanged. Evidence:
`corpus-import-receipts-2026-10-31.md` and `tests/test_import_receipts.py`.

View file

@ -0,0 +1,72 @@
{
"schema": "coulomb.native-mirror-worker-proof.v1",
"status": "native_worker_and_independent_receipt_verified",
"completed_at": "2026-10-31T20:27:08.869833+00:00",
"primary": {
"application": "coulomb-social",
"captured_at": "2026-10-31T20:21:01Z",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"version_id": "1793478121590106",
"ciphertext_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"ciphertext_bytes": 2181848,
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:522bcd7fce92436765a60c1e4afa2598f3cc40913352153ff1c4319a99ca92ed",
"database_sha256": "c5faa20ac0c443635393e04cf5b154710780d829d6c085edc098e2dd7525fc6a",
"content_sha256": "1bf31bafc19d8c4847f3793463189f13ce4b470ed96ccdc614b269acca6c12c0"
},
"recovery_mirror": {
"bucket": "railiance-coulomb-social-archives",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "91b4ffe4874a53e3422d604b7c3fa85f6551a481411ef82b2209993552486bfb",
"hash_readback_verified": true,
"object_key": "archives/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age",
"project_id": "06d107bc-9d84-4672-b2a0-95701cc0c07f",
"receipt_bytes": 1664,
"receipt_object_key": "archives/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T202101Z-2ad4faf215939d67e82f67a4.tar.age.741bdf7b02b859523053f45c88c874b58d43b45a0273bb315116d2bfc34e58aa.receipt.json",
"receipt_readback_verified": true,
"receipt_sha256": "741bdf7b02b859523053f45c88c874b58d43b45a0273bb315116d2bfc34e58aa",
"receipt_version_id": "1793478427159655",
"version_id": "1793478426025763"
},
"worker_results": [
{
"status": "mirrored_with_retained_receipt",
"primary_recaptured": false,
"ciphertext_readback_verified": true,
"receipt_readback_verified": true,
"recovery_key_read": false,
"independent_reader_used": false,
"retained_objects_deleted": false,
"application_restore_verified": false
},
{
"status": "mirrored_with_retained_receipt",
"primary_recaptured": false,
"ciphertext_readback_verified": true,
"receipt_readback_verified": true,
"recovery_key_read": false,
"independent_reader_used": false,
"retained_objects_deleted": false,
"application_restore_verified": false
}
],
"native_versions_before": 0,
"native_versions_after": 2,
"one_ciphertext_and_one_receipt_version_after_retry": true,
"independent_reader_ciphertext_and_receipt_verified": true,
"retained_receipt_equals_exact_public_field_filter": true,
"local_primary_receipt_and_ciphertext_unchanged": true,
"retry_last_mirror_bytes_unchanged": true,
"runtime_source_commit": "09f4fd33cbeefecb833998246cf53848bb1e5d8d",
"private_state_copy_used": true,
"production_application_contact": false,
"node_helpers_or_state_modified": false,
"node_timers_changed": false,
"native_capture_performed": false,
"decryption_identity_read": false,
"retained_objects_deleted": false,
"operating_capacity_profile_admitted": false,
"alert_reception_verified": false,
"scheduled_service_execution_verified": false,
"sources_manifest_sha256": "a865efe1cbf468c34e8f06912a6d423845ce08fc8a9963936f1e8af73897cad2"
}

View file

@ -0,0 +1,9 @@
/home/worsch/coulomb-social/deploy/backup/mirror-platform-runtime.json: 9253c5f6e956b67f499d3e12d2851be7fabd3bc56b00496b8810bbd0be8209f1
/home/worsch/coulomb-social/scripts/backup_capacity.py: cb699a79e67b129239b5aa5d6f3b0d4ffaa9773a8b073227ad17fdecb5b9d893
/home/worsch/coulomb-social/scripts/check-paired-backup-health.py: 2d40ec6e4c59ea7fe1d46065d197b9a87ca6811f023cc6f3e20e4184a3490566
/home/worsch/coulomb-social/scripts/mirror-paired-backup.py: 9ceb26d9083af016c4b787907e4dce7bdf437c782fb2d15eb28d9e334e6e431c
/home/worsch/railiance-platform/scripts/application_recovery_identities.py: 20f56255ed6eeec4aafe33f142bc3ee03f623d4957a7b0cabb741d340f15f79d
/home/worsch/railiance-platform/scripts/application_recovery_mirror.py: d48ab95e89b9eeae36269d853ebe4c44c22e1f57d4e14224f03d0b9aeeb34178
/home/worsch/railiance-platform/scripts/application_recovery_resources.py: ab70ec341e738272730eb0daf76840d0bca807caeb3c4e4c2357f162fbed4498
/home/worsch/railiance-platform/scripts/primary_application_archive.py: a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d
/tmp/csoc-mirror-content-point.py: bc4d04c3f8ee3ca089fa9b038e6ab617eaababcb7f0aec94339e7f4eeb70f13a

View file

@ -0,0 +1,72 @@
{
"schema": "coulomb.native-mirror-worker-proof.v1",
"status": "native_worker_and_independent_receipt_verified",
"completed_at": "2026-10-31T23:35:37.716717+00:00",
"primary": {
"application": "coulomb-social",
"captured_at": "2026-10-31T23:29:54Z",
"object_key": "platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"version_id": "1793489481116168",
"ciphertext_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"ciphertext_bytes": 2181848,
"age_recipient": "age1qm30jk8q0cezey652dk2dhcr3d9vs528v6lg8mavl6d4q3e3vgnqmrmyy3",
"image": "forgejo.coulomb.social/coulomb/coulomb-social@sha256:7906f461bc8d2d5204fd4fde67a251850f725b914452a66172919d6853672ed5",
"database_sha256": "4c49933e4fb81573257b7c64e95edd7f0395f20c9e6280348bb10383fd8eaa25",
"content_sha256": "0768e4f61ef003cff76c31aaca687e07e1743ff15a4d2d92dc0ebf123d4ec55e"
},
"recovery_mirror": {
"bucket": "railiance-coulomb-social-archives",
"ciphertext_bytes": 2181848,
"ciphertext_sha256": "eb495239fdf2344d6d72da9d4b02957613e7a4d67c54a2d67963ef802bc39421",
"hash_readback_verified": true,
"object_key": "archives/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age",
"project_id": "06d107bc-9d84-4672-b2a0-95701cc0c07f",
"receipt_bytes": 1664,
"receipt_object_key": "archives/platform-pg/application-archives/coulomb-social/2026/10/31/20261031T232954Z-039d1bab2ea7ef33b7453979.tar.age.1d1589e5e6dc5edc6354434bf851ffe07c65c92eb7fcf1ff296aea7bca67c91b.receipt.json",
"receipt_readback_verified": true,
"receipt_sha256": "1d1589e5e6dc5edc6354434bf851ffe07c65c92eb7fcf1ff296aea7bca67c91b",
"receipt_version_id": "1793489727604085",
"version_id": "1793489725683064"
},
"worker_results": [
{
"status": "mirrored_with_retained_receipt",
"primary_recaptured": false,
"ciphertext_readback_verified": true,
"receipt_readback_verified": true,
"recovery_key_read": false,
"independent_reader_used": false,
"retained_objects_deleted": false,
"application_restore_verified": false
},
{
"status": "mirrored_with_retained_receipt",
"primary_recaptured": false,
"ciphertext_readback_verified": true,
"receipt_readback_verified": true,
"recovery_key_read": false,
"independent_reader_used": false,
"retained_objects_deleted": false,
"application_restore_verified": false
}
],
"native_versions_before": 0,
"native_versions_after": 2,
"one_ciphertext_and_one_receipt_version_after_retry": true,
"independent_reader_ciphertext_and_receipt_verified": true,
"retained_receipt_equals_exact_public_field_filter": true,
"local_primary_receipt_and_ciphertext_unchanged": true,
"retry_last_mirror_bytes_unchanged": true,
"runtime_source_commit": "09f4fd33cbeefecb833998246cf53848bb1e5d8d",
"private_state_copy_used": true,
"production_application_contact": false,
"node_helpers_or_state_modified": false,
"node_timers_changed": false,
"native_capture_performed": false,
"decryption_identity_read": false,
"retained_objects_deleted": false,
"operating_capacity_profile_admitted": false,
"alert_reception_verified": false,
"scheduled_service_execution_verified": false,
"sources_manifest_sha256": "dc4648e896c65ba0efccb80ff3b545b8efd582e1c7f54b6152ee0194b5af9eda"
}

View file

@ -0,0 +1,9 @@
/home/worsch/coulomb-social/deploy/backup/mirror-platform-runtime.json: 9253c5f6e956b67f499d3e12d2851be7fabd3bc56b00496b8810bbd0be8209f1
/home/worsch/coulomb-social/scripts/backup_capacity.py: cb699a79e67b129239b5aa5d6f3b0d4ffaa9773a8b073227ad17fdecb5b9d893
/home/worsch/coulomb-social/scripts/check-paired-backup-health.py: 2d40ec6e4c59ea7fe1d46065d197b9a87ca6811f023cc6f3e20e4184a3490566
/home/worsch/coulomb-social/scripts/mirror-paired-backup.py: 9ceb26d9083af016c4b787907e4dce7bdf437c782fb2d15eb28d9e334e6e431c
/home/worsch/railiance-platform/scripts/application_recovery_identities.py: 20f56255ed6eeec4aafe33f142bc3ee03f623d4957a7b0cabb741d340f15f79d
/home/worsch/railiance-platform/scripts/application_recovery_mirror.py: d48ab95e89b9eeae36269d853ebe4c44c22e1f57d4e14224f03d0b9aeeb34178
/home/worsch/railiance-platform/scripts/application_recovery_resources.py: ab70ec341e738272730eb0daf76840d0bca807caeb3c4e4c2357f162fbed4498
/home/worsch/railiance-platform/scripts/primary_application_archive.py: a3f0a61e869a6012edb4b5288be14e3b0c4a9a1273b090320ab146d12e5aa94d
/tmp/csoc-mirror-editor-point.py: 2cba828db15a11415136eb17586c116ba10d06b835b5f8291ae858af7ad2b448

View file

@ -1,6 +1,6 @@
# Native password-reset bind prerequisite
WP-0015-T04 remains open. An isolated native Authelia/LLDAP test identifies a
WP-0015-T06 owns the remaining native authority return. An isolated native Authelia/LLDAP test identifies a
directory permission prerequisite before the full UC-009 browser journey can be
accepted. It does not change the existing personal reset acceptance, prove the
currently installed bind's privileges or authorize any live permission change.
@ -95,3 +95,237 @@ expiry/replay, KeyCape, user-engine HTTP and the actual app callback using an
explicit synthetic password-manager bind. See native-recovery-browser-review-2026-10-31.md.
That new proof does not grant production reset authority or rewrite this API-only
receipt's scope.
## Owner return assessed on October 31
The bind split and its post-split restore drill are now complete under
NK-WP-0054-T05. Their checks cover sign-in and MFA, not password replacement.
The founder receipt records svc-authelia as strict-readonly. Current read-only
metadata finds that bind in the ConfigMap and both deployed images exactly match
this isolated proof. No current directory membership or production reset was
queried, so this is an unresolved reset-authority return rather than a live
failure claim. See reset-owner-return-assessment-2026-10-31.json.
The same owner workplan now carries T09 (completed evidence assessment) and T10
(supported reset authority, reviewed execution and native/recovery acceptance).
CSOC-WP-0015-T06 consumes T10. T05 remains complete for its original migration
scope. Previous personal reset acceptance does not establish behavior after the
bind changed; no new grant is inferred from either the old approval or the
synthetic password-manager success. Preparation may proceed independently of the
pending application image and backup installation.
## Native authority matrix — October 31
`scripts/rehearse-reset-authority.py` extends the synthetic investigation without
changing the application, canonical bind helper, declaration or live directory.
It uses the same pinned native images and network-none loopback confinement.
Run with `--output` naming a fresh local JSON receipt; adjacent source manifest
and receipt refuse overwrite. Disabled assertions refuse before Docker access.
| Synthetic target | Strict-readonly | Password-manager |
| --- | --- | --- |
| Two ordinary users | Reset refused | Reset succeeds |
| Member of `lldap_admin` | Reset refused | Reset refused |
| Application-admin group member, outside `lldap_admin` | Reset refused | Reset succeeds |
| Other read-only service account, direct directory password API | Refused | Password changes |
The first four user cases are tested through actual Authelia reset/link flows
and direct directory password changes. Old/replacement password authentication
confirms every outcome; HTTP 200 is insufficient. Unverified requests and consumed
link replay refuse. Service interactive login through Authelia is excluded by the
filter in every scenario. This does not prohibit direct LLDAP web login, and that
filter does not constrain a compromised bind credential's directory password API.
For each role, seven valid directory mutations refuse: create/delete user,
edit another user's profile, create/delete group, self-promote to directory admin,
and remove another administrator's membership. Matching admin controls succeed,
and complete user/group metadata remains unchanged after the denied operations.
This bounds the tested operations; it does not enumerate every API right.
The LLDAP-admin restriction agrees with the pinned upstream
[LLDAP v0.6.2 integration guidance](https://github.com/lldap/lldap/blob/v0.6.2/README.md#general-configuration-guide).
The application-admin and service results matter to admission: a password-manager
role is not restricted to Coulomb users or to human accounts. It must not be
presented as restoring all prior reset behavior. No actual account's current
classification has been inferred from these fixtures.
Owner T11 records this completed rehearsal; T10 retains the supported runtime path.
Before an executable proposal, obtain owner-authorized, value-free classification
of the intended reset subjects and current bind memberships. Review a service-owned
password-write grant against SI-3 and its complete non-LLDAP-admin reach, and, if the accepted subject is a directory administrator, a
supported recovery route for that subject. Preserve the
existing human-admin separation and MFA requirements. A role change alone is not
a complete proposal, and no runtime grant or policy exception is approved here.
Evidence: reset-authority-matrix-2026-10-31.json and its source manifest;
reset-authority-validation-2026-10-31.json. Ten direct password cases, eight native
Authelia flows, fourteen unrelated-write denials and fourteen matching admin
controls pass. All disposable containers are removed. Both original native reset
scenarios still pass; the full `make check` gate passes 546 tests with zero
failures/errors/skips. These checks add neither productive acceptance nor a
current membership observation. Existing expiry/browser/app proof retains its
separate scope; this matrix does not retest the complete issuer/MFA journey.
## Current-role observation for the accepted account
The October6 acceptance record names **tegwick**
(`history/20261006T185411Z-personal-workspace-acceptance.md`). Decision
8770d4ea-8538-4f88-a9f8-8bcfb0542603 corrects the earlier inference that a new route
for all directory administrators must precede this consumer's remedy. Check
tegwick's current membership first. If it is an LLDAP administrator, its supported
recovery path is required; otherwise retain the administrator exclusion under
existing NetKingdom recovery ownership. The admitted password-write scope still
requires explicit review. This preserves the accepted workflow and adds no grant.
The fixed-subject observer is prepared and tested (owner T12); actual observation
is owner T13. Run these commands in your own workstation terminal:
```bash
install -d -m 700 /home/worsch/.local/state/net-kingdom/reset-authority
python3 /home/worsch/net-kingdom/sso-mfa/k8s/lldap/reset-authority-observe.py \
--receipt /home/worsch/.local/state/net-kingdom/reset-authority/roles-20261031.json
```
Enter the **human admin account's password** at the hidden prompt. The tool already
selects tegwick, svc-authelia and admin; it asks for no username, token or backup
credential. Do not paste the password into chat. Return only its JSON result.
The existing NetKingdom bind helper uses this human-held input for authenticated
reads; the new observer reuses its bounded transport without credential discovery.
Expected status is `current_reset_roles_observed`. It creates a mode0600 local
receipt, signs in as admin and performs three read queries. It changes no group,
password, declaration or deployment, sends no reset email, and grants nothing.
TLS failures and unexpected/missing users refuse; do not bypass verification.
An existing receipt refuses without overwrite. A successful result proves current
role metadata only, not Authelia reset success, app authorization or acceptance.
Evidence: reset-observer-preparation-2026-10-31.json. The observer and shared
transport pass92 checks; four native owner cases pass and the final observer node
passes with unchanged directory metadata and verified fixture cleanup. Production
observation has not been performed during preparation.
### Attended observation received
The founder completed the read at12:57:44Z on October31. Independent local receipt
verification confirms its private custody, exact subject/endpoint and all four
prepared source hashes. svc-authelia has only strict-readonly and no additional
groups. tegwick has no built-in directory privilege role and one other group.
Thus the directory-admin exclusion does not block the accepted personal flow on
this observed state. Owner T13 is complete; T10 prepares the reviewed transition,
including the full non-admin reach established by the native matrix. No grant,
reset, app authorization or productive acceptance is contained in this receipt.
Do not rerun the completed observer command. Evidence: reset-role-observation-
2026-10-31.json and reset-role-return-verification-2026-10-31.json.
## Reviewed role-transition proposal — 2026-10-31
**Prepared, not admitted or executed.** Owner preparation is NK-WP-0054-T14;
founder admission/execution is T15; declaration alignment and productive reset plus
fresh identity restore acceptance are T16. T10 and CSOC-WP-0015-T06 remain open.
Proposal: `sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f`. Exact controller and plan are
`/home/worsch/net-kingdom/sso-mfa/k8s/lldap/reset-role-transition.py` and
`reset-role-plan.json`. The digest binds the controller, existing transport and
observer sources, current role observation, native rights matrix, pinned runtime
identities/configuration and final validation. No new resources or recurring
capacity are requested; no credential rotation, service rename or restart is
included. Both pinned workloads were Ready when observed.
### Decision and scope
The proposed change replaces **only svc-authelia's strict-readonly membership**
with **lldap_password_manager**. This restores the native password-write capability
needed by the accepted tegwick recovery flow. The role can change passwords for
**all non-LLDAP-admin directory identities**, including other service accounts and
application administrators outside lldap_admin. It is not limited to Coulomb or to
human accounts. LLDAP administrators are excluded. Seven tested unrelated writes
remain refused; this is not an exhaustive rights audit. Both roles already permit
own-profile/password operations in the pinned [LLDAP access-control source](https://github.com/lldap/lldap/blob/v0.6.2/crates/auth/src/access_control.rs).
The same dedicated SERVICE account and single Authelia consumer remain. The plan
contains the proposed declaration: directory read USE plus narrowly described
password ADMINISTER scope, without the read-only bind convention. SI-3 permits a
service-owned write account with its narrowest working scope. The canonical
read-only declaration has not been changed to imply approval. Following a verified
grant, T16 must align it and existing bind verification/repair expectations. Do
not rerun the historical read-only bind migration as reset verification.
**Rollback limit:** membership rollback restores fresh Authelia reset denial, but
it does not revoke previously issued HTTP tokens or undo changed user passwords.
A native test proves a token obtained during the grant can still change an eligible
password after membership rollback; a freshly issued token cannot. The pinned
[LLDAP token implementation](https://github.com/lldap/lldap/blob/v0.6.2/server/src/auth_service.rs)
embeds groups in a token with a one-day expiry. Treat retained token authority as
lasting up to 24 hours unless separately revoked. This packet includes no token
revocation or incident-containment operation. Admission includes this limitation.
### Execution and recovery
Run in the founder's workstation terminal only after accepting the scope above:
```bash
python3 /home/worsch/net-kingdom/sso-mfa/k8s/lldap/reset-role-transition.py \
--execute --expect-digest sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f
```
The hidden prompt requires the human admin password. Never paste it into chat.
After reviewing the printed scope, enter exactly:
```text
GRANT sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f
```
The controller checks the exact live cluster/namespace/deployment identities,
images and Authelia configuration before private authentication and again after
confirmation. It rechecks the human admin role, tegwick's non-admin classification
and svc-authelia's exact baseline before writing. The old role is removed before
the new one is added; a brief directory lookup failure is possible. Every effect
has a durable intent record. All user IDs and group memberships are compared after
each step; unknown concurrent changes fail verification. Profile fields/passwords
are not copied into the receipt or claimed as independently compared.
Success is `password_manager_role_verified`. Paste only the returned JSON; Codex
will verify the durable mode0600 receipt in
`~/.local/state/net-kingdom/reset-authority/753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f/journal.json`.
An existing attempt refuses replay. Do not delete its state or repeat the grant.
If the operation is interrupted or reports uncertain status, restore read-only
membership with this separate attended recovery:
```bash
python3 /home/worsch/net-kingdom/sso-mfa/k8s/lldap/reset-role-transition.py \
--recover --expect-digest sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f
```
Confirm `RECOVER sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f`. For an already completed successful
grant, use `--rollback` instead of `--recover` and confirm
`ROLLBACK sha256:753f50f139a0d090a8b64ba15174f2a7e8b0e639eb0dd01d17783476b5a3759f`. Both restore only the role and retain
previous journal versions. They preserve unrelated subsequent memberships and
user password changes. Recovery tolerates workload unready status but still
requires pinned identities, images and configuration; runtime drift requires
review, not a bypass. If the directory itself is unreachable, recovery cannot
claim completion until the owner restores access. These are role restoration
commands with the token limitation above, not full credential revocation.
### Evidence and remaining acceptance
25 unit checks and four native controller checks pass with no skips/failures.
The native checks include real process termination immediately after removal and
addition, successful recovery without replay, and the retained-token boundary.
A separate confined native rehearsal uses **one running Authelia process** through
read-only → password-manager → read-only: reset refuses, succeeds, then refuses;
old/new passwords and consumed challenge denial are checked each time. No restart
is needed in this tested configuration. Synthetic fixtures and generated passwords
are removed; no provider SMTP or productive password reset was used.
Evidence: `reset-transition-validation-2026-10-31.json`,
`reset-role-refresh-2026-10-31.json` and its source manifest. A successful role
receipt still leaves T16: source alignment, productive tegwick reset/app/MFA and
fresh identity backup/restore evidence. It does not complete the four goal workplans.
The final consumer `make check` gate passes546 tests (zero failures/skips;69
existing warnings). Receipt: reset-transition-consumer-gate-2026-10-31.json.

View file

@ -0,0 +1,55 @@
{
"schema": "coulomb.operator-handoff-check.v1",
"recorded_at": "2026-10-31T16:35:39.718467+00:00",
"custody_preflight": {
"status": "completed_evidence_seal_preflight_verified",
"proposal_digest": "sha256:fa2763d1dcbe92fe638a29c9a7a7d51c03d30849c4232f2d43f81ef705bae611",
"entries": 8,
"modified": false,
"historical_operations_terminal": true
},
"route_preflight": {
"schema": "rapp-telemetry.backup-route-execution.v1",
"execution_digest": "sha256:4d8481061dc9ce6835169defc96887945052332a0db8d4234ae84032d63cc328",
"proposal_digest": "sha256:4bcad54f7b8e1e77b94112e452cbb58eaf8e882dc10305129c5f9ca69f014a66",
"status": "preflight_verified",
"timers_activated": false,
"notification_sent_by_helper": false,
"production_data_restored": false
},
"native_receipts_present": {
"custody": false,
"route": false,
"inactive_installation": false
},
"owner_tasks": {
"RPF-WP-0068-T03": "wait",
"RPF-WP-0068-T04": "todo",
"NK-reset-role-transition": "wait",
"CSOC-WP-0012-T07": "wait",
"CSOC-WP-0012-T08": "wait"
},
"decision_queue": [
{
"id": "3ec2f98e-9db3-47d9-bc1a-0387f980b81c",
"title": "Seal eight completed backup-evidence entries (fa2763d1)",
"status": "escalated",
"decision_type": "pending",
"decided_by": null,
"escalation_note": "Auto-escalated per constitution \u00a74: this pending decision touches financial or legal territory and requires explicit human approval before action."
},
{
"id": "5fa7234f-2a41-4eed-b4fc-9384f493572b",
"title": "Deploy recurring Coulomb backup alert route (4d848106)",
"status": "escalated",
"decision_type": "pending",
"decided_by": null,
"escalation_note": "Auto-escalated per constitution \u00a74: this pending decision touches financial or legal territory and requires explicit human approval before action."
}
],
"new_approval_recorded": false,
"operator_execution_performed": false,
"message_sent": false,
"timers_activated": false,
"existing_destinations_and_schedule_preserved": true
}

View file

@ -0,0 +1,42 @@
{
"schema": "coulomb.ordered-list-fidelity.v1",
"verified_at": "2026-10-31T17:20:32.943192+00:00",
"source_articles_sha256": "f33967c79c0f8a60a7d504f5d5e4082f8bc9e3c34f6a3160cb200fd380831d62",
"prior_audit_sha256": "3926e24a3b903f5e1d1c7133dc5f9df1e958183432c5131949ce838eb60acf1e",
"current_audit_sha256": "d8f382e24d7adfe3c43dc71f32f8f6e647bf68239cbcdc6769a6c8fb85d666b9",
"records": 245,
"rendered_cards_verified": 242,
"source_bodies_unchanged": true,
"missing_bodies_retained": 3,
"ordered_list_starts_restored": 15,
"affected_records": 4,
"markup_change_cases_before": 6,
"markup_change_cases_after": 3,
"remaining_removed_tags": {
"topic": 3
},
"remaining_removed_attributes": [
{
"tag": "topic",
"attribute": "name",
"count": 2
}
],
"private_review_directory": "~/.config/coulomb-social/auth/exports/rendering-review-20261031-numbering",
"source_hashes": {
"coulomb_social/apps/spaces/content.py": "5d6bb5c800b3e776fff385fa73284f4916403676dc2a8bc06b8fadc7d2387633",
"tests/test_rendering_audit.py": "ae092753f4d1d006bb88f1ce11581975c105c5231819f9409ddaaf2fc83d32f5"
},
"validation": {
"focused_passed": 14,
"full_gate_passed": 650,
"full_gate_warnings": 69,
"failures": 0,
"errors": 0,
"skipped": 0,
"full_gate_seconds": 91.85
},
"production_deployed": false,
"original_bubble_render_compared": false,
"productive_fidelity_accepted": false
}

View file

@ -72,8 +72,9 @@ applies to Authelia, not this additional consumer. SMTP AUTH proof must not subm
mail. The application sender migration needs a separate supervised configuration
executor and independent postflight after the scoped projection is Ready.
This candidate is **not execution-ready**: no owner admission helper/receipt or
supervised rollout packet exists for this new consumer yet. Resource-version and
This candidate is **not execution-ready**: the owner admission helper is now
prepared under RPF-WP-0068, but its native custody receipt and the separate
supervised sender rollout packet remain absent. Resource-version and
identity tests must be refreshed and re-reviewed at execution time. Do not apply
these individual patches by hand as a substitute for that packet.
@ -129,3 +130,20 @@ its global-recipient effect and release policy, and agree retention. It must ret
proof of why suppression changed and refuse unrelated/uncertain records. Productive
inbox access, selected-message authentication/correlation and the operator response
remain acceptance gates alongside the sender rollout.
## Sender transition owner follow-through
EMAIL-WP-0010 makes the separate configuration executor and native return explicit
owner tasks. Its completed T01 records a fresh six-table native ledger observation
at15:16:41Z, with no uncertain outcomes. Successful invitations may retain an
in_flight attempt row, so the observer follows the application rule that a delivery
receipt takes precedence. Fingerprints and counts share one read-only transaction;
the receiver remained live and this is not a drain or execution-time backup.
The source has no explicit SIGTERM drain handler. Remaining owner T02 must provide
a concrete supervised executor, fresh recoverable point, durable phase journal,
native postflight and recovery that preserves uncertain/accepted sends and later
writes. Existing admission, image, source and custody packets remain unchanged.
Evidence: `email-connect/docs/sender-transition-boundary-2026-10-31.md` and
`sender-ledger-native-2026-10-31.json` in the owner repository. The owner tests
report192passed/21existing optional-harness skips; all12new observer cases pass.

View file

@ -0,0 +1,21 @@
{
"schema": "coulomb.productive-copy-move-return.v1",
"review_date": "2026-11-01",
"helm_revision": 21,
"user": "Bernd",
"statement": "The step 2 of the walkthrough seems fine.",
"status": "owner_copy_move_workflow_accepted",
"review_guide": "docs/usecases/productive-content-acceptance-session.md#2-independent-copy-and-move--uc-004",
"accepted_scope": [
"Copy a page into another space",
"Edit the copy and detach its text attachment while the original body and downloadable file remain unchanged",
"Move another duplicate with its content, Visual and downloads intact",
"Previously saved address reaches the moved destination for the authorized owner"
],
"remaining_scope": [
"Moved destination and old-address denial for a participant lacking destination access (step3)",
"Sharing and revocation (step3)",
"Account-local portability and exit/succession semantics (step4)"
],
"complete_uc004_permission_verdict_inferred": false
}

View file

@ -0,0 +1,265 @@
{
"schema": "coulomb.native-reset-authority-matrix.v1",
"observed_at": "2026-10-31T12:37:24.533367+00:00",
"data": "synthetic",
"status": "native_authority_matrix_verified",
"images": {
"lldap": "sha256:9e605a66c02514bfcffd1b67cafb1e98d50992216bb2871d7ae44622047dd09d",
"authelia": "sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54",
"client": "sha256:512819463a1902b43a615beee14be7bf97e6a05e016385082b9380692ad1a8cd"
},
"production_access": false,
"provider_smtp_contacted": false,
"owner_runtime_or_permission_change_performed": false,
"no_ports_host_mounts_or_external_routes": true,
"backend_container_flags_verified_before_fixture_import": true,
"source_manifest_sha256": "c982e3a47d9e3aec5095a3c11fea7260fd4f5cb6db57b827b915988b432a18d0",
"target_classes": {
"alice": "ordinary user",
"bob": "other ordinary user",
"operator": "lldap_admin member",
"appoperator": "application-admins member, not lldap_admin",
"svc-other": "other service, lldap_strict_readonly"
},
"findings": {
"scenarios": [
{
"bind_role": "lldap_strict_readonly",
"directory_writes": {
"positive_controls": 7,
"refused_mutations": [
"create_user",
"edit_other_user",
"delete_user",
"create_group",
"delete_group",
"self_promote_admin",
"remove_admin_membership"
],
"directory_metadata_unchanged": true
},
"direct_password_changes": [
{
"target": "alice",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
},
{
"target": "bob",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
},
{
"target": "operator",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
},
{
"target": "appoperator",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
},
{
"target": "svc-other",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
}
],
"authelia_resets": [
{
"target": "alice",
"status": "reset_refused",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": "KO",
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": true,
"new_password_accepted_after": false
},
{
"target": "bob",
"status": "reset_refused",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": "KO",
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": true,
"new_password_accepted_after": false
},
{
"target": "operator",
"status": "reset_refused",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": "KO",
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": true,
"new_password_accepted_after": false
},
{
"target": "appoperator",
"status": "reset_refused",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": "KO",
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": true,
"new_password_accepted_after": false
}
]
},
{
"bind_role": "lldap_password_manager",
"directory_writes": {
"positive_controls": 7,
"refused_mutations": [
"create_user",
"edit_other_user",
"delete_user",
"create_group",
"delete_group",
"self_promote_admin",
"remove_admin_membership"
],
"directory_metadata_unchanged": true
},
"direct_password_changes": [
{
"target": "alice",
"password_changed": true,
"old_password_accepted": false,
"new_password_accepted": true
},
{
"target": "bob",
"password_changed": true,
"old_password_accepted": false,
"new_password_accepted": true
},
{
"target": "operator",
"password_changed": false,
"old_password_accepted": true,
"new_password_accepted": false
},
{
"target": "appoperator",
"password_changed": true,
"old_password_accepted": false,
"new_password_accepted": true
},
{
"target": "svc-other",
"password_changed": true,
"old_password_accepted": false,
"new_password_accepted": true
}
],
"authelia_resets": [
{
"target": "alice",
"status": "reset_completed",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": null,
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": false,
"new_password_accepted_after": true
},
{
"target": "bob",
"status": "reset_completed",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": null,
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": false,
"new_password_accepted_after": true
},
{
"target": "operator",
"status": "reset_refused",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": "KO",
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": true,
"new_password_accepted_after": false
},
{
"target": "appoperator",
"status": "reset_completed",
"only_loopback_interface": true,
"external_routes_absent": true,
"old_password_initially_accepted": true,
"new_password_initially_refused": true,
"native_first_factor": true,
"interactive_service_login_refused": true,
"reset_http_status": 200,
"reset_response_status": null,
"native_identity_finish": true,
"reset_without_verified_challenge_refused": true,
"consumed_identity_token_replay_refused": true,
"old_password_accepted_after": false,
"new_password_accepted_after": true
}
]
}
],
"cleanup_verified": true
}
}

View file

@ -0,0 +1,3 @@
scripts/rehearse-reset-authority.py: 120d3b8d4e54ec87386f9f2d53abe836feab48c7cc883a13e0d4672bcc347b0c
scripts/rehearse-native-password-reset.py: 133c8230c524fbbaf7f66e4872e14b826ed2c5a4f4de3bc47fa86cfef7631d5d
scripts/native_recovery/probe.py: eab3bae55626abd4920eb6aa2e329c83559007c54ac74c3aaa1cf41e84eb6556

View file

@ -0,0 +1,48 @@
{
"schema": "coulomb.reset-authority-validation.v1",
"status": "source_and_isolated_native_verified",
"full_gate": {
"command": "make check",
"tests": 546,
"failures": 0,
"errors": 0,
"skipped": 0,
"junit_sha256": "ff99e36bad4e42a1983bdac2749b834a454a22c708065752ecd31f8be5ac963a"
},
"native_matrix": {
"roles": 2,
"direct_password_cases": 10,
"authelia_reset_cases": 8,
"unrelated_write_denials": 14,
"valid_admin_write_controls": 14,
"cleanup_verified": true
},
"legacy_native_reset_regression": "passed both original role scenarios",
"cli_refusals": [
"disabled assertions",
"existing receipt",
"existing source manifest"
],
"current_live_membership_verified": false,
"production_reset_verified": false,
"authority_grant_approved": false,
"sources": [
{
"path": "scripts/rehearse-reset-authority.py",
"sha256": "120d3b8d4e54ec87386f9f2d53abe836feab48c7cc883a13e0d4672bcc347b0c"
},
{
"path": "scripts/native_recovery/probe.py",
"sha256": "eab3bae55626abd4920eb6aa2e329c83559007c54ac74c3aaa1cf41e84eb6556"
},
{
"path": "docs/release/reset-authority-matrix-2026-10-31.json",
"sha256": "89c555ac2c580153bd81ae03c662d024a0cdbeba173f93e6ebfa041a1a0261c7"
},
{
"path": "docs/release/reset-default-regression-2026-10-31.json",
"sha256": "82efb79154b5e5003ce4b821ebeba73deb4375a13f4df93eddbd3b8908e705ea"
}
],
"upstream_reference": "https://github.com/lldap/lldap/blob/v0.6.2/README.md#general-configuration-guide"
}

Some files were not shown because too many files have changed in this diff Show more