Independent API-layer workspace nested inside the contracts repository.
packages/client: TypeChain-first client, provider routing, policy metadata, and generated wrappers.packages/api: versioned HTTP API, domain modules, workflow routes, auth, rate limits, and contract execution context.packages/indexer: Base event ingestion, Supabase/Postgres projection pipeline, and reorg handling.db: Supabase/Postgres migrations, RLS policies, and GraphQL-first schema artifacts.scenario-adapter: API-mode shims plus a copied local snapshot of the full contracts deployment-scenario suite.ops: Local orchestration and health tooling.
ALL work MUST be documented in CHANGELOG.md. No exceptions.
We are committed to a "Building in Public" approach. This means every minor and major milestone, architectural change, or feature addition throughout the project's life must be recorded. Documentation is not just for devs; it's the history of the repo's evolution.
pnpm install
pnpm run sync:scenarios
pnpm run codegen
pnpm run buildThe API runtime reads the repo .env directly. The deployed proof path is bound to the values already verified there:
DIAMOND_ADDRESSRPC_URLALCHEMY_RPC_URLALCHEMY_API_KEYCHAIN_IDNETWORKPRIVATE_KEYORACLE_WALLET_PRIVATE_KEYAPI_LAYER_KEYS_JSONAPI_LAYER_SIGNER_MAP_JSON
For Base Sepolia proof runs, use the repo .env instead of hand-pointing the test harness:
pnpm run baseline:show
pnpm run setup:base-sepolia
pnpm run test:contract:base-sepoliaThose commands read the repo .env, verify the configured chain id from the live RPC, and fail loudly if the repo config is incomplete.
Licensing is split into two lifecycles:
- Template lifecycle:
createTemplate -> updateTemplate/setTemplateStatus -> createLicenseFromTemplateThis derives voice-specific terms and does not create a caller-scoped active license. - Active license lifecycle:
issueLicenseorcreateLicenseThis creates the caller-scoped license that powersget-license-terms,record-licensed-usage, andtransfer-license.
Caller-scoped licensing reads and actions must use the licensee actor, not a generic read/admin actor.
Governance workflows are also split by live chain timing:
POST /v1/workflows/submit-proposalPOST /v1/workflows/vote-on-proposal
The API no longer treats proposal submission and voting as a single happy-path workflow because the live Base Sepolia governance baseline enforces a non-zero voting delay.
pnpm run setup:base-sepolia writes a best-effort fixture report to .runtime/base-sepolia-operator-fixtures.json. That setup pack uses the repo .env signers to inspect and prepare:
- buyer funding and USDC allowance
- licensee/transferee actors
- aged marketplace listing fixtures
- proposer role and voting-power state
It reports which fixtures are actually ready, which are partial, and which still require upstream contract state or additional testnet funding.
Business rule: a voice asset can only be commercialized by the current on-chain owner of that asset.
Not sufficient on their own:
- collaborator role
- marketplace seller role
- per-voice authorization
- template access
- approval-style setup without ownership
Commercialization includes:
- dataset packaging using the asset
- listing the resulting dataset/asset for sale
- collaborator-driven commercialization wrappers
UI precondition copy:
- Only the current asset owner can commercialize this asset.
- This account is authorized, but not the owner.
- Transfer ownership first if this collaborator should commercialize.
Validation guidance:
- Fail early in the workflow/UI when the acting user is not the owner.
- Suggested error shape: "commercialization requires current asset ownership; actor is authorized but not owner; transfer asset ownership before commercialization".
scenario:api no longer falls back to local Anvil happy paths. You must supply an explicit scenario command:
API_LAYER_SCENARIO_COMMAND="node scenario-adapter/trace_access_bootstrap_invariants.js" pnpm run scenario:apiTo bind that command to the validated Base Sepolia deployment baseline:
API_LAYER_SCENARIO_COMMAND="node scenario-adapter/trace_access_bootstrap_invariants.js" pnpm run scenario:api:base-sepoliaIf a scenario uses multiple contract-signing actors, map signer addresses to API keys with API_LAYER_SIGNER_API_KEYS_JSON so API-mode calls preserve caller identity instead of collapsing to one global key.
GET /v1/transactions/requests/:requestId requires x-api-key and a UUID request ID. It returns only records created for that authenticated owner and logical signer; wallet headers, shared wallet addresses, display labels and administrator roles do not grant access. Missing, unauthorized and legacy ownerless records all return 404. Public on-chain receipt lookup remains public.
Apply db/migrations/0008_transaction_request_owner.sql before deploying the updated API. For API-key rotation, configure a stable, opaque requestOwnerId in the server-owned API-key record and retain the same logical signerId. Without this optional ID, each key owns only its own requests. Old ownerless records are preserved and cannot be assigned by guessing from wallet/label metadata. See the privacy build log for migration, key rotation and rollback behavior.