Architecture
What This Page Covers
This page explains what Market 2.0 is designed to do, why key model choices were made, how the system works end to end, and what problems it does not try to solve.
Purpose
Market 2.0 is the common interaction layer between clients and storage providers.
It is designed so PoRep-style storage requests, PDP requests, and future specialized requests all use one stable deal envelope instead of separate APIs per request type.
Core Deal Envelope
type Deal struct {
Identifier ulid.ULID `json:"identifier"`
Client string `json:"client"`
Data *DataSource `json:"data,omitempty"`
Products Products `json:"products"`
}
type Products struct {
DDOV1 *DDOV1 `json:"ddo_v1,omitempty"`
RetrievalV1 *RetrievalV1 `json:"retrieval_v1,omitempty"`
PDPV1 *PDPV1 `json:"pdp_v1,omitempty"`
}Field meaning:
identifier: deal reference.client: requester identity.products: requested behavior.data: operation input when required.
Product-specific structs and fields are documented in:
Why client Is Text
client Is Textclient is intentionally a text field, not a strict Filecoin address type.
Why:
Market 2.0 must support identity schemes beyond native Filecoin address formats.
Current CurioAuth support is Filecoin-address based.
L2 or product-specific identity formats can be supported later without changing the deal envelope.
Identity safety comes from authentication and identity matching, not from enforcing one address type in the schema.
Deal Identifier (ULID)
identifier uses ULID.
ULID specification: https://github.com/ulid/spec
Implementations (many languages): https://github.com/ulid/spec#implementations-in-other-languages
Go implementation: https://github.com/oklog/ulid
JavaScript/TypeScript implementation: https://github.com/ulid/javascript
ULID is used as a portable, sortable, globally unique deal reference.
Authentication and Authorization
This section documents current auth behavior.
Header format:
Authorization: CurioAuth <keyType>:<base64(addressBytes)>:<base64(signatureBytes)>
Supported key types:
secp256k1blsdelegated
Signing input:
Build message bytes as
addressBytes || uppercaseRequestMethod || escapedRequestPath || RFC3339MinuteTimestamp.Hash with SHA-256.
Sign the digest.
addressBytes means Filecoin address bytes (address.Address.Bytes()), not the human-readable address string. escapedRequestPath is the request URL path without the query string.
The request body and query string are not part of the signed message. The auth header is therefore a short-lived path-scoped credential and must be protected in transit.
Verification window:
Current minute.
Previous minute, to tolerate requests crossing a minute boundary in transit.
Authorization checks:
Signature must verify for the request method, request path, and one of the accepted timestamps.
Client allow/deny policy is read from
market_mk20_clients.If client is not listed, fallback behavior uses
DenyUnknownClientsconfig.For routes with
{id}, authenticated client must own that deal.
Route scope:
Market API routes are authenticated.
/info/*OpenAPI routes are public.
Data Model
Validation rules:
piece_cidmust be valid PieceCID v2.Exactly one source type is allowed.
Exactly one format is allowed.
Source type must be enabled by provider policy.
Aggregation
Current aggregation support:
AggregateTypeV1only.This is datasegment/PODSI-style aggregation based on FRC-0058: https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0058.md
Why aggregation matters in Market 2.0:
It allows subpiece-aware indexing and retrieval flows.
It enables retrieval of subpieces, including IPFS-style single-CID usage from subpiece data paths when retrieval/indexing settings are enabled.
Current constraints:
Aggregate-of-aggregate is rejected.
source_aggregatemode and pre-aggregatedformat.aggregate.submode have different validation rules.Subpiece order is meaningful and must match segment order.
Future direction:
Additional aggregation types can be added as new FRCs are finalized.
The top-level Market 2.0 deal envelope remains unchanged.
Product Composition Rules
At least one top-level product must be present.
ddo_v1andpdp_v1cannot be used together in one deal.ddo_v1requiresretrieval_v1.With
ddo_v1,retrieval_v1.announce_piecemust be false.
How a Deal Moves Through the System
Client submits deal intent.
Market 2.0 validates identity, payload, and product composition.
Market 2.0 selects product execution path.
Market 2.0 selects ingest path (immediate source or upload-first).
Processing continues through common lifecycle states.
Deal reaches terminal success (
complete) or terminal failure (failed).
Lifecycle states:
accepteduploadingprocessingsealingindexingfailedcomplete
External Contract Boundary
Market 2.0 supports product-specific contract verification without changing the outer deal model.
Example:
DDO verifies through
CurioDealViewV1, primarily withversion()andverifyDeal(...).The interface also exposes
getDealState(...)for deal-state queries.Provider allowlist controls which contracts are accepted.
Interface versioning is explicit.
Best Practices
Keep
identifierimmutable.Keep
clientstable within the chosen identity namespace.Query provider capabilities before creating deals.
Keep product payloads explicit.
Vet contracts before allowlisting.
Extend existing products only when backward compatibility is preserved.
Add a new product when behavior or lifecycle contracts diverge.
What Market 2.0 Does Not Solve
Commercial negotiation and legal agreement terms.
Automatic trust in external contracts.
Universal payment/settlement semantics across all products.
Product-specific business policy decisions outside declared product contracts.
Operational monitoring and recovery by itself.
Last updated