Federation via Credit Commons Protocol: Cross-Bank Time Exchange
Summary
This issue proposes implementing the Credit Commons Protocol (CC) in TimeOverflow to enable cross-bank exchanges between independent TimeOverflow instances — and potentially with any other CC-compatible mutual credit system — without a centralised database.
Background & Motivation
TimeOverflow is deployed as independent instances (one per time bank). Members of one bank currently cannot exchange time with members of another bank. This is a fundamental limitation for the movement.
Several approaches have been considered:
- Blockchain: Rejected. The "trustless" property is a category error for time banks, which are inherently trust-based communities. Operational burden on small orgs is prohibitive, and time credits are not fungible tokens.
- Centralised federation hub: Works (CES uses this model with "Clearing Central") but creates a single point of failure and political control.
- Signed receipts + PKI (bilateral): Sound approach, but reinvents a wheel that Credit Commons already designs well.
- Credit Commons Protocol: ✅ Best fit. Designed exactly for this problem, has a published OpenAPI spec (v0.9.0), an active reference implementation in PHP, and a growing community.
The Credit Commons whitepaper (Slater & Jenkin, 2016) describes the model as "a money system that is locally controlled but globally useful" — which maps directly to TimeOverflow's architecture.
What is the Credit Commons Protocol?
Credit Commons is an open protocol for federated mutual credit accounting. Key properties:
- Tree structure: Ledgers are arranged in a fractal tree (trunk → branches → twigs → leaves). Each TimeOverflow instance would be a twig; its members are leaves.
- No shared database: Each node maintains its own ledger. Only mirror accounts (one per peer connection) need to stay synchronised.
- Cryptographic ratchet: Node-to-node integrity is maintained via a hash chain on mirror accounts. The last hash serves as both authentication and sync check — no PKI certificates needed.
- Two-phase transactions: A validate phase (nothing written) followed by a write phase (on user confirmation). Handles distributed atomicity cleanly.
- Sovereign nodes: Each bank keeps full autonomy over its membership rules, balance limits, and governance. The protocol doesn't prescribe policy.
- Protocol, not platform: Like HTTP, any compliant implementation can participate. A Rails implementation of CC would be a significant contribution to the wider movement.
The protocol is described in detail at https://credit-commons.gitlab.io/credit-commons-documentation/ and the OpenAPI 3.0 spec is at https://gitlab.com/credit-commons-software-stack/cc-php-lib.
API Overview (v0.9.0)
The spec has ~12 endpoints split into two layers:
Client layer (user-facing, consumed by TimeOverflow UI)
| Method |
Path |
Purpose |
| GET |
/forms |
List available transaction workflows |
| GET |
/accounts |
Autocomplete account names across the tree |
| GET |
/account |
Account stats (balance, volume, trades) |
| GET |
/account/history |
Balance history for charts |
| GET |
/transactions |
Filter/list transactions |
| GET |
/entries |
Filter/list transaction entries |
| GET |
/transaction/{uuid} |
Retrieve a single transaction |
| GET |
/about |
Node info: conversion rate, stats, currency format |
Core transversal layer (node-to-node federation)
| Method |
Path |
Purpose |
| POST |
/transaction |
Client proposes a transaction |
| POST |
/transaction/relay |
Node-to-node relay (the core federation endpoint) |
| PATCH |
/transaction/{uuid}/{dest_state} |
Advance transaction through workflow states |
For a minimal viable implementation, only 3 endpoints are essential: POST /transaction/relay, PATCH /transaction/{uuid}/{dest_state}, and GET /about.
Authentication
Two security schemes:
- HumanUser: JWT bearer token with scopes (
passive, active, helper, admin).
- OtherNode: Custom
Last-hash header — the last hash from the mirror account's hash chain. This is simultaneously authentication AND integrity check. No certificates or OAuth required.
# Example node-to-node request header
Last-hash: sha256:a3f9c2...
If the hash doesn't match, the server returns a HashMismatch CCFailure and the connection is considered broken.
Transaction Lifecycle
# 1. Client proposes (Alice@BankA paying Bob@BankB)
POST /transaction
body: { payer: "alice", payee: "bankb/bob", quant: 2.0,
description: "Garden help", workflow: "+|PPC-PE-CE=" }
# 2. BankA relays to BankB (validate phase — nothing written yet)
POST /transaction/relay [Last-hash: <hash>]
→ 200 + { secs_valid_left: 300 } # validated, held temporarily
# 3. Alice confirms
PATCH /transaction/{uuid}/C
→ 201 # written on all nodes
# On failure:
POST /transaction/relay
→ 500 + { errors: [{ class: "HashMismatch", node: "bankb" }] }
Workflow DSL
Transactions carry a compact workflow code, e.g.:
| Code |
Meaning |
+|PPC-PE-CE= |
Payee credits payer, payer confirms |
-|PPC-PE+CE- |
Payer bills payee, payee approves |
0_CCE= |
Admin creates transaction directly |
The typical timebanking flow (+|PPC-PE-CE=) maps cleanly to TimeOverflow's existing offer/confirm model.
Implementation Proposal
Scope: Twig-only (Phase 1)
Each TimeOverflow instance acts as a twig node. Members are leaves. A lightweight trunk node (which could be another TimeOverflow instance in trunk mode, or a thin standalone service) manages the inter-bank meta-ledger. This mirrors the CES/Clearing Central model but with a proper CC-compliant protocol.
This is deliberately conservative: get bilateral cross-bank exchange working first, then expand to multi-bank tree routing.
Data Model Changes
# New model: FederationPeer
# Represents a trusted peer node (another time bank)
create_table :federation_peers do |t|
t.string :name, null: false
t.string :base_url, null: false
t.string :last_hash_sent, null: false, default: ''
t.string :last_hash_received, null: false, default: ''
t.float :conversion_rate, null: false, default: 1.0
t.string :status # active / suspended / pending
t.timestamps
end
# New columns on transactions table:
add_column :transactions, :cc_uuid, :string
add_column :transactions, :cc_state, :string # P/V/C/E/X
add_column :transactions, :cc_workflow, :string
add_column :transactions, :is_transversal, :boolean, default: false
add_column :transactions, :federation_peer_id, :references
# New model: IntertradinAccount (one per FederationPeer)
# A virtual member account whose balance = net position with that peer.
# Sum of all intertrading accounts should always net to zero.
New Routes
# config/routes.rb
namespace :credit_commons, path: 'cc' do
get 'about' # node discovery
get 'accounts' # account autocomplete
get 'account' # account stats
post 'transaction/relay' # node-to-node relay
get 'transaction/:uuid', action: :show
patch 'transaction/:uuid/:dest_state', # state transitions
action: :transition
end
New Service Objects
app/services/credit_commons/
hash_ratchet.rb # Compute, store, verify last_hash per peer
transaction_relay.rb # Outbound relay to peer nodes (HTTP client)
workflow_parser.rb # Decode workflow DSL strings
address_resolver.rb # Resolve "othertimebank/alice" paths
node_client.rb # Wraps Faraday/Net::HTTP calls to peer nodes
New Controllers
app/controllers/credit_commons/
relay_controller.rb # POST /cc/transaction/relay
# PATCH /cc/transaction/:uuid/:state
about_controller.rb # GET /cc/about
account_controller.rb # GET /cc/account
Middleware
A CreditCommons::NodeAuthMiddleware rack middleware validates the Last-hash header on all /cc/* node-to-node requests, updates the ratchet on success, and returns a HashMismatch CCFailure on mismatch.
Integration Point in Existing Flow
When a user creates a transaction and the payee's address contains a / (indicating a remote account, e.g. othertimebank/bob), the existing transaction creation flow branches:
# app/services/transaction_creator.rb (existing, modified)
if payee.transversal?
CreditCommons::TransactionRelay.new(transaction, peer).call
else
# existing local flow unchanged
end
Known Hard Problems
The CC protocol documentation itself flags these as open/unsolved in v1:
- Transaction ordering: Two simultaneous transactions from different nodes may arrive at a mirror account in different order, breaking the ratchet. Proposed mitigation: trunkward node determines write order (requires a serialisation buffer).
- Node offline mid-write-phase: If a peer goes offline between validate and write, the transaction is in an ambiguous state. Mitigation: idempotent writes + a catch-up/reconciliation endpoint (not yet in spec).
- Rounding errors: Conversion between units at different decimal precision. Mitigation: trunkward node determines the authoritative amount.
For Phase 1 (bilateral, low-volume time banks), problems 1 and 2 are low-risk and can be handled with simple retry logic and manual admin reconciliation.
What We Are NOT Doing (Yet)
- Multi-hop tree routing (Phase 1 is bilateral / single-trunk only)
- Conversion rates (time is time: 1 hour = 1 hour everywhere)
- Business logic microservice (no fees, no automatic entries)
- A full governance/voting UI for federation agreements
Relationship to Existing CC Ecosystem
- Reference implementation:
cc-node (PHP). We would implement the protocol in Rails independently, but against the same OpenAPI spec — meaning TimeOverflow nodes would be interoperable with cc-node deployments and any other CC-compatible system.
- Contact: Matthew Slater (@matslats) is the protocol author and actively seeking implementors. A Rails implementation would be a significant contribution to the CC community.
- Mutual Credit Services (https://mutualcredit.services) is building commercial deployments on CC — potential collaboration/testing partner.
Suggested Implementation Order
GET /cc/about — simplest endpoint, enables node discovery, good first PR
FederationPeer model + HashRatchet service — foundation for everything else
POST /cc/transaction/relay (inbound) — receive transactions from peers
PATCH /cc/transaction/:uuid/:state — write phase / state transitions
- Outbound relay in transaction creation flow
GET /cc/account, GET /cc/accounts — needed for cross-bank member lookup in UI
- Admin UI for managing federation peer agreements
References
Federation via Credit Commons Protocol: Cross-Bank Time Exchange
Summary
This issue proposes implementing the Credit Commons Protocol (CC) in TimeOverflow to enable cross-bank exchanges between independent TimeOverflow instances — and potentially with any other CC-compatible mutual credit system — without a centralised database.
Background & Motivation
TimeOverflow is deployed as independent instances (one per time bank). Members of one bank currently cannot exchange time with members of another bank. This is a fundamental limitation for the movement.
Several approaches have been considered:
The Credit Commons whitepaper (Slater & Jenkin, 2016) describes the model as "a money system that is locally controlled but globally useful" — which maps directly to TimeOverflow's architecture.
What is the Credit Commons Protocol?
Credit Commons is an open protocol for federated mutual credit accounting. Key properties:
The protocol is described in detail at https://credit-commons.gitlab.io/credit-commons-documentation/ and the OpenAPI 3.0 spec is at https://gitlab.com/credit-commons-software-stack/cc-php-lib.
API Overview (v0.9.0)
The spec has ~12 endpoints split into two layers:
Client layer (user-facing, consumed by TimeOverflow UI)
/forms/accounts/account/account/history/transactions/entries/transaction/{uuid}/aboutCore transversal layer (node-to-node federation)
/transaction/transaction/relay/transaction/{uuid}/{dest_state}For a minimal viable implementation, only 3 endpoints are essential:
POST /transaction/relay,PATCH /transaction/{uuid}/{dest_state}, andGET /about.Authentication
Two security schemes:
passive,active,helper,admin).Last-hashheader — the last hash from the mirror account's hash chain. This is simultaneously authentication AND integrity check. No certificates or OAuth required.If the hash doesn't match, the server returns a
HashMismatchCCFailureand the connection is considered broken.Transaction Lifecycle
Workflow DSL
Transactions carry a compact workflow code, e.g.:
+|PPC-PE-CE=-|PPC-PE+CE-0_CCE=The typical timebanking flow (
+|PPC-PE-CE=) maps cleanly to TimeOverflow's existing offer/confirm model.Implementation Proposal
Scope: Twig-only (Phase 1)
Each TimeOverflow instance acts as a twig node. Members are leaves. A lightweight trunk node (which could be another TimeOverflow instance in trunk mode, or a thin standalone service) manages the inter-bank meta-ledger. This mirrors the CES/Clearing Central model but with a proper CC-compliant protocol.
This is deliberately conservative: get bilateral cross-bank exchange working first, then expand to multi-bank tree routing.
Data Model Changes
New Routes
New Service Objects
New Controllers
Middleware
A
CreditCommons::NodeAuthMiddlewarerack middleware validates theLast-hashheader on all/cc/*node-to-node requests, updates the ratchet on success, and returns aHashMismatchCCFailure on mismatch.Integration Point in Existing Flow
When a user creates a transaction and the payee's address contains a
/(indicating a remote account, e.g.othertimebank/bob), the existing transaction creation flow branches:Known Hard Problems
The CC protocol documentation itself flags these as open/unsolved in v1:
For Phase 1 (bilateral, low-volume time banks), problems 1 and 2 are low-risk and can be handled with simple retry logic and manual admin reconciliation.
What We Are NOT Doing (Yet)
Relationship to Existing CC Ecosystem
cc-node(PHP). We would implement the protocol in Rails independently, but against the same OpenAPI spec — meaning TimeOverflow nodes would be interoperable with cc-node deployments and any other CC-compatible system.Suggested Implementation Order
GET /cc/about— simplest endpoint, enables node discovery, good first PRFederationPeermodel +HashRatchetservice — foundation for everything elsePOST /cc/transaction/relay(inbound) — receive transactions from peersPATCH /cc/transaction/:uuid/:state— write phase / state transitionsGET /cc/account,GET /cc/accounts— needed for cross-bank member lookup in UIReferences