Skip to content

Federation via Credit Commons Protocol: Cross-Bank Time Exchange #846

@rewritten

Description

@rewritten

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:

  1. 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).
  2. 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).
  3. 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

  1. GET /cc/about — simplest endpoint, enables node discovery, good first PR
  2. FederationPeer model + HashRatchet service — foundation for everything else
  3. POST /cc/transaction/relay (inbound) — receive transactions from peers
  4. PATCH /cc/transaction/:uuid/:state — write phase / state transitions
  5. Outbound relay in transaction creation flow
  6. GET /cc/account, GET /cc/accounts — needed for cross-bank member lookup in UI
  7. Admin UI for managing federation peer agreements

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions