Add content-addressable runs and activities

- compute_run_id() for deterministic run identification
- /assets/by-run-id/{run_id} endpoint for L1 recovery
- Store run_id in provenance when recording runs
- Activities now use content hash as ID instead of UUID
- URLs: /activities/{content_hash} instead of /activities/1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-09 11:04:59 +00:00
parent 0f01d8e12c
commit 3ed4fe89ed
2 changed files with 66 additions and 7 deletions

22
db.py
View File

@@ -65,9 +65,9 @@ CREATE TABLE IF NOT EXISTS assets (
updated_at TIMESTAMPTZ
);
-- Activities table
-- Activities table (activity_id is content-addressable run_id hash)
CREATE TABLE IF NOT EXISTS activities (
activity_id UUID PRIMARY KEY,
activity_id VARCHAR(64) PRIMARY KEY,
activity_type VARCHAR(50) NOT NULL,
actor_id TEXT NOT NULL,
object_data JSONB NOT NULL,
@@ -83,8 +83,8 @@ CREATE TABLE IF NOT EXISTS anchors (
tree_ipfs_cid VARCHAR(128),
ots_proof_cid VARCHAR(128),
activity_count INTEGER NOT NULL,
first_activity_id UUID,
last_activity_id UUID,
first_activity_id VARCHAR(64),
last_activity_id VARCHAR(64),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
confirmed_at TIMESTAMPTZ,
bitcoin_txid VARCHAR(64)
@@ -243,6 +243,20 @@ async def get_asset_by_hash(content_hash: str) -> Optional[dict]:
return None
async def get_asset_by_run_id(run_id: str) -> Optional[dict]:
"""Get asset by run_id stored in provenance."""
async with get_connection() as conn:
row = await conn.fetchrow(
"""SELECT name, content_hash, ipfs_cid, asset_type, tags, metadata, url,
provenance, description, origin, owner, created_at, updated_at
FROM assets WHERE provenance->>'run_id' = $1""",
run_id
)
if row:
return _parse_asset_row(row)
return None
async def get_all_assets() -> dict[str, dict]:
"""Get all assets as a dict indexed by name."""
async with get_connection() as conn: