Skip to content

About

Mutual-credit ledger with trustlines, routed payments, pure Ruby calculations and a Rails API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

FOAF protocol: Ruby implementation

FOAF (Friend of a Friend) is a protocol for community exchange through mutual credit. Participants agree how much credit to extend to one another. Payments update balances along those relationships, including paths through intermediaries who permit routing.

This repository implements the credit ledger as a Rails HTTP service with its own PostgreSQL database. GrowOperative, a local-food exchange application, is its first consumer. The application handles listings and exchange workflows; this service handles trustlines, transfer calculations and ledger records.

Protocol documentation · FOAF Foundation

Current scope

The Ruby service is under active development. The repository includes:

  • Currency networks and bilateral trustlines, with agreed credit limits.
  • Direct and multi-hop transfer calculations, pathfinding and routing fees.
  • Pending transfers that the receiver can confirm or reject, and the sender can cancel.
  • Idempotency keys for pending-transfer creation and repeatable confirmation.
  • Operation records and events for inspecting ledger changes.
  • Detection and cancellation of circular debt, called credit loops.

The broader architecture proposes Rust/Scrypto ports and additional protocol modules. Those are not implemented in this repository. The current service persists to PostgreSQL; it does not execute transfers on a blockchain.

A small example

Alice and Bob start with a zero balance. Bob extends Alice a credit limit of 100 units. Alice pays Bob 25 units for produce. The balance from Alice's perspective becomes -25, leaving 75 units of available credit. Bob sees the opposite balance, +25.

Run this calculation from the repository root with Ruby, without Rails or a database:

require "./foaf_trustline/lib/foaf/trustline/protocol"

math = Foaf::Trustline::Protocol::BalanceMath
balance = math.apply_direct_transfer(
  balance: BigDecimal("0"),
  value: BigDecimal("25"),
  creditline_received: BigDecimal("100")
)

puts balance.to_s("F") # -25.0
puts math.capacity(
  balance: balance,
  creditline_received: BigDecimal("100")
).to_s("F") # 75.0

These cases are covered in the balance math specs. For a routed payment, each hop must have enough capacity; the protocol layer calculates the changes and the Rails service persists them together.

Architecture

Consuming application
        |
        | HTTP /api/v1
        v
Rails controllers
        |
        v
Services: transfer coordination, transactions and row locks
        |                              |
        v                              v
Pure Ruby protocol math          ActiveRecord / PostgreSQL
                                 trustlines, operations, events

The boundary is deliberate: protocol calculations take values and return results or errors. They do not load Rails or access the database. Services handle persistence and call that math inside database transactions. This lets the calculation specs run alone and gives a future implementation in another language a set of reference cases.

Location Responsibility
Pure protocol layer Balance math, fees, trustline state transitions, multi-hop calculations and credit-loop detection
Services Transfer execution, signature verification and credit-loop coordination
Models Persistent ledger records and protocol parameters
API controllers HTTP requests and responses
Routes Current API surface
Protocol specs Calculation and state-machine examples without Rails
Request specs API contracts, pending-transfer retries and signature enforcement

TransferService locks the affected trustlines in canonical order and writes balance changes, the operation and its events within one transaction. Pending-transfer confirmation locks the pending record, joins that transaction and returns the existing result when a confirmed transfer is retried. The request specs exercise that retry behavior.

Identity and current defaults

Participants are identified by Ethereum-style addresses. The service can recover a signer's address from a secp256k1 signature supplied in the X-Signature header over the raw request body.

Signature enforcement defaults to off. The signature_enforcement protocol parameter must be set to "true" to activate the controller checks. Reads are public. The API also includes key generation and recovery endpoints that return private key material to the caller.

See the API controller, signature verifier and key endpoints.

Local development

The Docker setup uses Ruby 3.4.11, Rails 8.1 and PostgreSQL 16. Install Docker with Compose, then run from the repository root:

docker compose build foaf
docker compose up -d db
docker compose run --rm foaf bundle exec rails db:prepare
docker compose up -d foaf
curl http://localhost:3002/api/v1/version

The API listens on port 3002 and PostgreSQL is mapped to host port 5434. The supplied Compose file uses development credentials and publishes both ports; use it only in a trusted local environment. Initial seeds create protocol parameters, not example currency networks or users.

Stop the local services with docker compose down. The named database volume is retained.

Tests

After building the image, the pure protocol specs need no database:

docker compose run --rm --no-deps foaf bundle exec rspec spec/protocol

For the complete suite, start the database and prepare the separate test database:

docker compose up -d db
docker compose run --rm -e RAILS_ENV=test foaf bundle exec rails db:test:prepare
docker compose run --rm -e RAILS_ENV=test foaf bundle exec rspec

The test configuration derives a test database URL from the development URL, or accepts an explicit DATABASE_URL_TEST. The CI workflow runs the complete RSpec suite against PostgreSQL on pull requests.

About

Mutual-credit ledger with trustlines, routed payments, pure Ruby calculations and a Rails API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages