- Remove default password fallback from POSTGRES_PASSWORD in docker-compose.yml
- Remove default password fallback from db.py and migrate.py
- Update .env.example with required POSTGRES_PASSWORD
- Update README to mark DATABASE_URL as required
Co-Authored-By: Claude Opus 4.6 <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>
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>
- 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>
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>
- 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>
The db now returns datetime objects instead of strings in some cases.
Added format_date() helper function that handles both datetime and string
values, and replaced all [:10] date slicing with calls to this helper.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All publishing operations now use three-phase atomic approach:
1. Phase 1: Preparation - validate inputs, gather IPFS CIDs
2. Phase 2: IPFS operations - pin all content before any DB changes
3. Phase 3: DB transaction - all-or-nothing database commits
Changes:
ipfs_client.py:
- Add IPFSError exception class
- Add add_bytes() to store content on IPFS
- Add add_json() to store JSON documents on IPFS
- Add pin_or_raise() for synchronous pinning with error handling
db.py:
- Add transaction() context manager for atomic DB operations
- Add create_asset_tx() for transactional asset creation
- Add create_activity_tx() for transactional activity creation
- Add get_asset_by_hash_tx() for lookup within transactions
- Add asset_exists_by_name_tx() for existence check within transactions
server.py:
- Rewrite record_run:
- Check L2 first for inputs, fall back to L1
- Store recipe JSON on IPFS with CID in provenance
- Auto-register input assets if not already on L2
- All operations atomic
- Rewrite publish_cache:
- IPFS CID now required
- Synchronous pinning before DB commit
- Transaction for asset + activity
- Rewrite _register_asset_impl:
- IPFS CID now required
- Synchronous pinning before DB commit
- Transaction for asset + activity
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ipfs_cid column was being stored but not retrieved in queries,
causing the UI to show "Not on IPFS" even when the data exists.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add IPFS container to docker-compose
- Add ipfshttpclient dependency
- Add ipfs_client.py module for IPFS operations
- Add ipfs_cid field to Asset model and database schema
- Pin content on L2 IPFS when assets are published/registered
L2 now stores content on its own IPFS node, enabling
federation - content remains available even if L1 goes down.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Parse JSONB fields (provenance, origin, tags, metadata, object_data, signature)
if they come back as strings instead of dicts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
asyncpg requires datetime objects, not ISO strings. Added _parse_timestamp
helper to convert string timestamps to datetime objects.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PostgreSQL with asyncpg for persistent storage
- Create db.py module with async database operations
- Create migrate.py script to migrate JSON data to PostgreSQL
- Update docker-compose.yml with PostgreSQL service
- Home page now shows README with styled headings
- Remove /ui prefix routes, use content negotiation on main routes
- Add /activities/{idx} as canonical route (with /activity redirect)
- Update /assets/{name} to support HTML and JSON responses
- Convert auth.py to use async database operations
- RSA keys still stored as files in $ARTDAG_DATA/keys/
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>