- Replace 3765-line monolithic server.py with 26-line entry point
- All routes now in app/routers/ using Jinja2 templates
- Backup old server as server_legacy.py
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Provides /help index and /help/{doc_name} routes to view
L1 server and Common library READMEs in the web UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
/docs now correctly points to FastAPI's Swagger API docs.
README files can be viewed directly in the git repository.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update README with comprehensive documentation covering ActivityPub,
OpenTimestamps anchoring, L1 integration, and all API endpoints
- Add /docs routes to serve markdown documentation as styled HTML
- Include common library documentation in web interface
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create app factory with routers and templates
- Auth, assets, activities, anchors, storage, users, renderers routers
- Federation router for WebFinger and nodeinfo
- Jinja2 templates for L2 pages
- Config and dependency injection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Accept both numeric index and activity_id hash
- Look up activity by ID from database when hash provided
- Refactor ui_activity_detail to support both lookup methods
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When asset already exists, check if activity exists too.
If no activity, create one for the existing asset.
This fixes the case where an asset was registered but the
activity creation failed or was skipped.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add get_asset_by_name_tx for transaction-aware asset lookup
- Use transaction connection instead of separate connection
- Prevents race condition where asset might not be visible
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Database: Add description field, remove unique constraint to allow
multiple configs of same provider type
- UI: Main page shows provider types as cards with counts
- UI: Per-type page (/storage/type/{type}) for managing configs
- API: Add get_user_storage_by_type() for filtered queries
- Form: Add description field for distinguishing configs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both /storage/{id}/test and DELETE /storage/{id} were using Bearer
token auth only. Now they also check cookie auth for browser sessions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added 4 new storage providers:
- NFT.Storage (free for NFT data)
- Infura IPFS (5GB free)
- Filebase (5GB free, S3-compatible IPFS)
- Storj (25GB free, decentralized cloud)
Updated UI with 7 total storage options in a 4-column grid,
each with distinct colored borders for visibility.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The POST /storage endpoint required Bearer token auth and JSON body,
which didn't work with browser form submissions using cookies. Added
new /storage/add endpoint that accepts form data and cookie auth.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The /storage route was only checking Bearer token authentication,
causing logged-in browser users to be redirected to login. Now also
checks cookie authentication like other HTML pages.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The storage option buttons were nearly invisible due to low contrast
between bg-dark-600 buttons and bg-dark-700 background. Added distinct
colored borders (blue/green/purple) and darker backgrounds to make
the Pinata, web3.storage, and Local Storage options clearly visible.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Buttons had same background color as container, making them nearly
invisible. Added border-dark-500 and hover:border-blue-500.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same content negotiation fix as other endpoints - default to HTML
for browsers, only return JSON if explicitly requested.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 of distributed storage implementation:
Database:
- user_storage table for storage providers (Pinata, web3.storage, local)
- storage_pins table to track what's stored where
- source_url/source_type columns on assets for reconstruction
Storage Providers:
- Abstract StorageProvider base class
- PinataProvider for Pinata IPFS pinning
- Web3StorageProvider for web3.storage
- LocalStorageProvider for filesystem storage
- Factory function create_provider()
API Endpoints:
- GET/POST /storage - list/add storage providers
- GET/PATCH/DELETE /storage/{id} - manage individual providers
- POST /storage/{id}/test - test connectivity
UI:
- /storage page with provider cards
- Add provider form (Pinata, web3.storage, local)
- Test/remove buttons per provider
- Usage stats (capacity, donated, used, pins)
50% donation model: half of user capacity is available for
system use to store shared content across the network.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
L2 now calls /auth/revoke-user (revokes by username) instead of
/auth/revoke (revokes by token), because L1 has scoped tokens that
differ from L2's own token.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On logout:
1. Call /auth/revoke on each attached L1 renderer
2. Remove all attachments from user_renderers table
3. Clear L2 cookie
This ensures logging out of L2 also logs user out of all L1s.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When L1 successfully calls /auth/verify, the user's attachment to
that L1 server is recorded in the user_renderers table.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
L1 servers must now identify themselves when calling /auth/verify.
Only servers listed in L1_SERVERS can verify tokens.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
L1 servers now verify tokens by calling L2's /auth/verify endpoint,
so L2 no longer needs to share cookies across subdomains. Each L1
sets its own first-party cookie via its /auth endpoint.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add user_renderers table to track which L1 servers users are attached to
- Add L1_SERVERS config to define available renderers
- Add /renderers page showing attachment status for each L1 server
- Add attach functionality (redirects to L1 /auth with token)
- Add detach functionality with HTMX updates
- Add Renderers link to nav bar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Login page accepts return_to URL. After login, redirects to
return_to with auth_token in URL so target site can set its
own first-party cookie (works around iOS Safari ITP).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change SameSite from Lax to None to allow cookie to be sent
when navigating between L1 and L2 subdomains. iOS Safari's
Intelligent Tracking Prevention may block Lax cookies.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Download links now point to /cache/{hash}/raw which returns proper
Content-Type headers and filename with extension.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When publishing to L2, if the asset already exists, return it with
existing: true flag instead of raising a 400 error. This makes
re-publishing idempotent and handles race conditions gracefully.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After L1 content negotiation fix, /cache/{hash} returns HTML for
browsers. Embedded <img> tags need /raw to get actual image data.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- RecordRunRequest.output_name now optional (deprecated)
- Input assets named by their content_hash
- Output assets named by their content_hash
- Simplified registered_inputs - just content_hash and ipfs_cid
- All assets now referenced by content_hash, not friendly names
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All public endpoints (/assets/{name}, /activities/{id}, /objects/{hash})
now default to HTML for browsers. JSON/ActivityStreams is only returned
when explicitly requested via Accept header. Fixes browser link clicks
showing JSON instead of HTML pages.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When auto-registering input assets from a run, detect recipes
and give them appropriate names (recipe-{hash}) and tags
(auto-registered, input, recipe) instead of generic input names.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Activity IDs are now VARCHAR(64) content hashes, not UUIDs.
Removed all UUID() conversions and updated SQL casts from uuid[]
to text[].
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Recipe assets now display their YAML source code in a formatted
code block instead of just a download link.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Include artdag-client.tar.gz package
- Add /download/client route to serve the package
- Add "Download Client" link in nav bar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>
- Add styled 404 page for HTML requests (returns JSON for API calls)
- Add Web UI section to README documenting available routes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The README on the home page uses prose classes which require the
typography plugin to render headers and other markdown elements.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>