feat: require auth for registry endpoints and track asset ownership

- Add authentication to /registry endpoint
- Add authentication to /registry/record-run endpoint
- Extract register logic to _register_asset_impl helper
- Store owner username in registered assets
- Use authenticated user for ActivityPub actor ID in activities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-07 17:25:15 +00:00
parent d4ae129b3a
commit 9a0e77a852

View File

@@ -889,9 +889,8 @@ async def get_asset(name: str):
return registry["assets"][name] return registry["assets"][name]
@app.post("/registry") def _register_asset_impl(req: RegisterRequest, owner: str):
async def register_asset(req: RegisterRequest): """Internal implementation for registering an asset."""
"""Register a new asset and create ownership activity."""
registry = load_registry() registry = load_registry()
# Check if name exists # Check if name exists
@@ -908,6 +907,7 @@ async def register_asset(req: RegisterRequest):
"metadata": req.metadata, "metadata": req.metadata,
"url": req.url, "url": req.url,
"provenance": req.provenance, "provenance": req.provenance,
"owner": owner,
"created_at": now "created_at": now
} }
@@ -921,7 +921,7 @@ async def register_asset(req: RegisterRequest):
activity = { activity = {
"activity_id": str(uuid.uuid4()), "activity_id": str(uuid.uuid4()),
"activity_type": "Create", "activity_type": "Create",
"actor_id": f"https://{DOMAIN}/users/{USERNAME}", "actor_id": f"https://{DOMAIN}/users/{owner}",
"object_data": { "object_data": {
"type": req.asset_type.capitalize(), "type": req.asset_type.capitalize(),
"name": req.name, "name": req.name,
@@ -930,7 +930,7 @@ async def register_asset(req: RegisterRequest):
"algorithm": "sha3-256", "algorithm": "sha3-256",
"value": req.content_hash "value": req.content_hash
}, },
"attributedTo": f"https://{DOMAIN}/users/{USERNAME}" "attributedTo": f"https://{DOMAIN}/users/{owner}"
}, },
"published": now "published": now
} }
@@ -946,9 +946,15 @@ async def register_asset(req: RegisterRequest):
return {"asset": asset, "activity": activity} return {"asset": asset, "activity": activity}
@app.post("/registry")
async def register_asset(req: RegisterRequest, user: User = Depends(get_required_user)):
"""Register a new asset and create ownership activity. Requires authentication."""
return _register_asset_impl(req, user.username)
@app.post("/registry/record-run") @app.post("/registry/record-run")
async def record_run(req: RecordRunRequest): async def record_run(req: RecordRunRequest, user: User = Depends(get_required_user)):
"""Record an L1 run and register the output.""" """Record an L1 run and register the output. Requires authentication."""
# Fetch run from L1 server # Fetch run from L1 server
try: try:
resp = requests.get(f"{L1_SERVER}/runs/{req.run_id}") resp = requests.get(f"{L1_SERVER}/runs/{req.run_id}")
@@ -972,15 +978,15 @@ async def record_run(req: RecordRunRequest):
"rendered_at": run.get("completed_at") "rendered_at": run.get("completed_at")
} }
# Register the output # Register the output under the authenticated user
return await register_asset(RegisterRequest( return _register_asset_impl(RegisterRequest(
name=req.output_name, name=req.output_name,
content_hash=output_hash, content_hash=output_hash,
asset_type="video", # Could be smarter about this asset_type="video", # Could be smarter about this
tags=["rendered", "l1"], tags=["rendered", "l1"],
metadata={"l1_run_id": req.run_id}, metadata={"l1_run_id": req.run_id},
provenance=provenance provenance=provenance
)) ), user.username)
# ============ Activities Endpoints ============ # ============ Activities Endpoints ============