Skip to content

feat: ingest a ledger schema (YAML) and materialize it on a target ledger #158

Description

@altitude

Problem

The docs site ships a handful of curated example ledger schemas (chart of accounts + Numscript transactions + sample queries) as YAML — see apps/docs/content/schemas/ in formancehq/docs (e.g. stablecoin-operations.yaml, payment-acceptance.yaml, omnibus.yaml, issuing-financial-host.yaml). A reader who likes one of these has no first-class path from "I read the page" to "this is running on a ledger I control." Today the workflow is:

  1. Copy each transaction out of the docs by hand.
  2. fctl ledger create <name>.
  3. fctl ledger send <name> --numscript '<each script…>' one at a time.
  4. fctl ledger accounts set-metadata for any static metadata.

That's a lot of friction for a "try this recipe" loop.

Proposal

Add a schema-ingest command, e.g.

fctl ledger apply <ledger> --schema <path-or-url-to.yaml>

That command would:

  • Create the ledger if it doesn't exist (or --ledger-exists=skip|error).
  • Walk the YAML and seed account metadata for every static account in the chart (the templated $var accounts are created lazily by transactions, nothing to do).
  • Optionally run a seed: block of transactions (e.g. for opening balances, demo fixtures).
  • Be idempotent — re-running against a partially-applied ledger should be a no-op for the parts already in place.

Schema shape is already prototyped in the docs schemas — happy to share / lift into a public spec. Rough sketch:

chart:
  platform:
    pivot:
      stablecoin_issuance:
        .metadata: { type: "platform_pivot" }
  clients:
    \$client_id:
      .pattern: '^[a-zA-Z0-9_-]+\$'
      stablecoin:
        .metadata: { type: "user_stablecoin_balance" }

transactions:
  ONRAMP_MINT_CONFIRMATION:
    description: "Issue stablecoin against pivot account"
    script: |
      vars { ... }
      send [USD/2 100.00] (
        source = @platform:pivot:stablecoin_issuance
        destination = @clients:\$client_id:stablecoin
      )

seed:
  - tx: ONRAMP_MINT_CONFIRMATION
    vars: { client_id: "demo-1" }

Why fctl

This could live as a separate tool, but fctl is the natural home — it's already the place readers come to set up a ledger, and the existing primitives (ledger create, ledger send, ledger accounts set-metadata) are exactly what schema-apply orchestrates. Doing it server-side (in the ledger itself) is more work and locks the schema format; doing it in fctl keeps the format malleable.

Out of scope (probably)

  • A schema registry / sharing format — that's a separate conversation. For now a flat YAML file referenced by path or URL is enough.
  • Migrations between schema versions. v0 is "apply once."

Context

Surfaced while documenting the stablecoin operations cookbook recipe. Without fctl ledger apply, the page can document the schema but can't give the reader a one-liner to try it. With it, the docs page can render a "Run on a ledger" affordance that resolves to a single fctl command.

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