FPSF-CPP-001 — CashPack Protocol
Document Metadata
| Field | Value |
|---|---|
| Spec ID | FPSF-CPP-001 |
| Title | CashPack Protocol |
| Version | 1.0.0 |
| Status | Draft |
| Date | 2026-03-25 |
| Author | Adalton Reis — reis@fabricpaymentstandards.org |
| Organization | Fabric Payment Standards Foundation |
| Contact | specs@fabricpaymentstandards.org> |
| License | Apache-2.0 |
| Adopts | FPSF-CPD-001 v1.0.0 |
Scope
This document specifies the CashPack Protocol (CPP-1.0): a standard for issuing, transferring, and redeeming signed bearer instruments over any settlement infrastructure. It defines:
- All data structures and their fields
- Cryptographic algorithms and requirements
- Protocol obligations for conforming Operators at issuance, renewal, and redemption
- The relationship between the CashPack instrument and the FPSF-CPD-001 Payment model
- Versioning and extension rules
- Normative references
Out of scope: key management architectures (see FPSF-MPC-001), banking system integration, currency exchange, inter-Operator settlement (Phase 2), and any distributed consensus mechanism.
1. Normative Language
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL are to be interpreted as described in RFC 2119.
2. Relationship to FPSF-CPD-001
This specification adopts the Canonical Payment Definition (FPSF-CPD-001 v1.0.0) as its foundational payment model. The CashPack instrument represents a specialized Digital Payment in which:
- The Payer is the Principal who locks funds at issuance
- The Payee is the final bearer who redeems the instrument
- The Value is the locked amount and currency
- The AuthorizationProof is the cryptographic signature chain embedded in the instrument's
renewal_chainand at each operation - The PaymentState lifecycle maps as follows:
| CPD-001 State | CashPack Equivalent |
|---|---|
CREATED | Lock Request submitted; instrument not yet issued |
AUTHORIZED | Operator issues signed instrument (status: ACTIVE) |
IN_FLIGHT | Instrument in transit between bearers (renewal in progress) |
SETTLED | Instrument redeemed (status: REDEEMED) |
FAILED | Instrument expired (status: EXPIRED) |
CANCELLED | Instrument cancelled by Operator (status: CANCELLED) |
All CPD-001 invariants (uniqueness, integrity, authorization binding, deterministic state, finality) MUST be preserved by conformant Operators.
3. Cryptographic Requirements
| Primitive | Requirement |
|---|---|
| Asymmetric signing | Ed25519 REQUIRED. ECDSA P-256 OPTIONAL for implementations with HSM constraints. |
| Hash function | SHA-256 for all chain digest computation. |
| Key encoding | Base64url (RFC 4648 §5), no padding. |
| Signature encoding | Base64url, no padding. |
| Canonical JSON | RFC 8785 (JCS) MUST be applied to all JSON objects before signing. |
| Timestamp format | ISO 8601 / RFC 3339, UTC. Example: 2026-03-25T14:30:00Z. |
3.1 Chain Digest Computation
chain_digest[0] = SHA256(canonical_json(lock_request))
chain_digest[n] = SHA256(chain_digest[n-1] || canonical_json(renewal_entry[n]))
|| denotes concatenation of raw bytes.
3.2 Disposable Key Pairs
Bearer keys SHOULD be disposable — generated for a single instrument and discarded after the instrument is renewed or redeemed. The FPSF-MPC-001 system is the RECOMMENDED mechanism for generating disposable bearer keys in high-privacy deployments.
4. Data Structures
4.1 Lock Request
Constructed by the Principal. Signed with the Principal's private key. Submitted to the Issuance endpoint.
| Field | Type | Description |
|---|---|---|
version | string | MUST be "CPP-1.0". |
request_id | string (UUIDv4) | Unique ID generated by the Principal. |
timestamp | string (RFC 3339) | Creation time. |
operator_id | string | Unique identifier of the target Operator. |
principal_pk | string (Base64url) | Principal's public key. |
initial_bearer_pk | string (Base64url) | Public key of the intended first bearer. |
amount | integer | Amount in smallest currency unit (e.g., cents). |
currency | string (ISO 4217) | e.g., "USD", "EUR", "BRL". |
expiry | string (RFC 3339) | Expiry set by Operator policy. |
memo_hash | string (hex, optional) | SHA-256 of a private memo. Not stored in plaintext. |
principal_signature | string (Base64url) | Ed25519 signature over canonical JSON of all fields above. |
4.2 CashPack Instrument
Constructed by the Operator at issuance. Operator-signed. Updated at each renewal.
| Field | Type | Description |
|---|---|---|
version | string | MUST be "CPP-1.0". |
pack_id | string (UUIDv4) | Globally unique. Assigned by Operator at issuance. |
operator_id | string | Issuing Operator identifier. |
amount | integer | Locked amount. Immutable after issuance. |
currency | string (ISO 4217) | Currency. Immutable after issuance. |
issued_at | string (RFC 3339) | Operator's issuance timestamp. |
expiry | string (RFC 3339) | Expiry datetime. |
status | string (enum) | ACTIVE, REDEEMED, EXPIRED, or CANCELLED. |
current_bearer_pk | string (Base64url) | Public key of the current authorized bearer. |
lock_request | object | Full original Lock Request object. |
renewal_chain | array | Ordered array of Renewal Entry objects. Empty at issuance. |
chain_digest | string (hex) | Current chain digest. |
operator_signature | string (Base64url) | Operator signature over canonical JSON of all above fields (excluding this field). |
4.3 Renewal Entry
Constructed by the outgoing bearer. Counter-signed by the Operator. Appended to renewal_chain.
| Field | Type | Description |
|---|---|---|
renewal_id | string (UUIDv4) | Unique ID for this renewal. |
timestamp | string (RFC 3339) | Renewal request time. |
outgoing_bearer_pk | string (Base64url) | Current bearer's public key. MUST match current_bearer_pk. |
incoming_bearer_pk | string (Base64url) | New bearer's public key. |
prev_chain_digest | string (hex) | Chain digest before this renewal. |
outgoing_bearer_signature | string (Base64url) | Signature over canonical JSON of all above fields. |
operator_renewal_signature | string (Base64url) | Operator countersignature over canonical JSON of all fields including bearer signature. |
4.4 Redemption Request
Constructed by the current bearer. Signed with the redeemer's private key.
| Field | Type | Description |
|---|---|---|
pack_id | string (UUIDv4) | Instrument being redeemed. |
timestamp | string (RFC 3339) | Request time. |
redeemer_pk | string (Base64url) | Redeemer's public key. MUST match current_bearer_pk. |
destination | object | Operator-defined payout destination schema. |
redeemer_signature | string (Base64url) | Signature over canonical JSON of all above fields. |
5. Protocol Obligations
5.1 Issuance
- Operator MUST authenticate the Principal (account holder verification).
- Operator MUST verify
principal_signatureon the Lock Request. - Operator MUST verify available balance ≥
amount. - Operator MUST verify
amount≤ Operator's configured maximum. - Operator MUST lock the amount before issuing the instrument.
- Operator MUST set
current_bearer_pk=initial_bearer_pkfrom the Lock Request. - Operator MUST compute
chain_digest[0]=SHA256(canonical_json(lock_request)). - Operator MUST sign the full instrument and return it to the Principal.
5.2 Renewal
- Operator MUST verify its own prior
operator_signatureon the submitted instrument. - Operator MUST verify instrument
status=ACTIVEand expiry has not passed. - Operator MUST verify
outgoing_bearer_pk=current_bearer_pk. - Operator MUST verify
outgoing_bearer_signatureon the Renewal Entry. - Operator MUST verify
prev_chain_digestmatches the instrument's currentchain_digest. - Operator MUST verify renewal chain depth has not exceeded its configured maximum.
- Operator MUST update
current_bearer_pk=incoming_bearer_pk. - Operator MUST compute the new
chain_digestand re-sign the updated instrument. - Operator MUST record the
renewal_idand MUST reject duplicaterenewal_idvalues (replay prevention).
5.3 Redemption
- Operator MUST verify the full instrument signature chain.
- Operator MUST verify
redeemer_pk=current_bearer_pk. - Operator MUST verify
redeemer_signatureon the Redemption Request. - Operator MUST verify instrument
status=ACTIVEand not expired. - Operator MUST verify the redeemer's identity per its compliance policy.
- Operator MUST mark the instrument
REDEEMEDbefore releasing funds. - Operator MUST release locked funds to the specified destination.
6. Instrument Status Values
| Status | Terminal | Description |
|---|---|---|
ACTIVE | No | Instrument is valid; may be renewed or redeemed. |
REDEEMED | Yes | Redeemed; funds released. |
EXPIRED | Yes | Reached expiry without redemption; funds returned to Principal. |
CANCELLED | Yes | Cancelled by Operator (typically in response to a legal order); funds held. |
7. Mandatory API Endpoints
| Endpoint | Description |
|---|---|
POST /v1/cashpack/issue | Submit a Lock Request. Returns a signed instrument on success. |
POST /v1/cashpack/renew | Submit an instrument plus a Renewal Entry. Returns updated signed instrument. |
POST /v1/cashpack/redeem | Submit a Redemption Request plus the instrument. Returns Redemption Confirmation. |
GET /v1/cashpack/{pack_id}/status | Query instrument status. Restricted to issuing Principal and Operator. |
8. Transport Requirements
- All endpoints MUST be served over TLS 1.3 or higher.
Content-Type: application/jsonon all requests and responses.- The
issueandstatusendpoints MUST require authenticated sessions. - The
renewandredeemendpoints accept unauthenticated sessions; bearer authorization is derived from cryptographic signature verification.
9. Well-Known Resources
| URL | Purpose |
|---|---|
/.well-known/cashpack-policy.json | Operator Policy Document (see Governance layer). |
/.well-known/cashpack-pubkey.json | Operator's current public signing key in JWK format (RFC 7517). |
10. Versioning and Extensions
The version field is set to "CPP-1.0" for this specification.
- Future versions increment the minor version for backward-compatible additions.
- Future versions increment the major version for breaking changes.
- Operators MUST reject instruments with unrecognized major versions.
- Extensions MAY be added in an
extensionsobject within any structure and MUST be ignored by non-recognizing implementations.
11. Error Codes
| Error Code | Meaning |
|---|---|
INSUFFICIENT_BALANCE | Principal's available balance is less than the requested amount. |
AMOUNT_EXCEEDS_LIMIT | Amount exceeds the Operator's configured maximum. |
INVALID_SIGNATURE | One or more signatures failed verification. |
BEARER_MISMATCH | Submitting public key does not match current_bearer_pk. |
CHAIN_DIGEST_MISMATCH | prev_chain_digest does not match the instrument's current chain_digest. |
INSTRUMENT_NOT_ACTIVE | Instrument status is REDEEMED, EXPIRED, or CANCELLED. |
CHAIN_DEPTH_EXCEEDED | renewal_chain has reached the Operator's configured maximum depth. |
REDEEMER_NOT_IDENTIFIED | Redeemer could not be verified to the required identity level. |
DUPLICATE_ID | request_id or renewal_id has been seen before (replay prevention). |
EXPIRY_INVALID | Requested expiry is outside the Operator's permitted range. |
12. Conformance
A conformant Operator implementation MUST:
- Accept and process Lock Requests per Section 5.1
- Accept and process Renewal Entries per Section 5.2
- Accept and process Redemption Requests per Section 5.3
- Enforce all cryptographic requirements of Section 3
- Expose all mandatory API endpoints of Section 7
- Publish a Policy Document at the well-known URL defined in Section 9
- Preserve all CPD-001 invariants as mapped in Section 2
Normative References
| Reference | Description |
|---|---|
| FPSF-CPD-001 v1.0.0 | Canonical Payment Definition |
| RFC 2119 | Key words for use in RFCs to Indicate Requirement Levels |
| RFC 3339 | Date and Time on the Internet: Timestamps |
| RFC 4648 | The Base16, Base32, and Base64 Data Encodings |
| RFC 7517 | JSON Web Key (JWK) |
| RFC 8032 | Edwards-Curve Digital Signature Algorithm (EdDSA) |
| RFC 8785 | JSON Canonicalization Scheme (JCS) |
| ISO 4217 | Currency Codes |
| FIPS 180-4 | Secure Hash Standard (SHA-2 family) |
FPSF-CPP-001 v1.0.0 · Draft · Fabric Payment Standards Foundation · Apache-2.0