Fibric. Docs fibric.io →
v1.0.0 · stable
Build

Local development

The fibric CLI ships a complete local runtime: an in-process kernel with the same router, executor, and trust evaluation as the platform, backed by in-memory seams and a file-backed secret stub. The loop is edit, watch the envelope stream react, replay fixtures, read the dispositions, with no deployment between you and the answer. This page covers the dev server, tailing envelopes, fixture replay, hot reload, and the debugging tools; the CLI reference documents every flag, and testing connectors covers assertions and CI.

The development loop

Scaffold once, then live in two terminals:

terminal
# once
fibric init brightdesk --template connector --name cn-brightdesk
cd brightdesk && npm install

# terminal 1: the local kernel, watching your source
fibric dev

# terminal 2: push envelopes through it and read what happened
fibric dev replay fixtures/sample.jsonl

Because the local kernel runs the real machinery, what you observe locally is what production does: the router matches the same glob triggers, the executor takes the same single-flight locks and dedupes the same idempotency keys, and the trust policy evaluates default-closed from ./policy/trust.yaml. The only substitutions are at the seams: in-memory bus and store, stubbed side effects, file-backed secrets.

The dev server

fibric dev loads every def in the project, prints what it registered, and stays up watching:

terminal
$ fibric dev
  fibric dev 0.9  ·  local kernel  ·  tenant t_local
  loaded connector cn-brightdesk@1.0.0 (2 tools, 1 event)
  executor: in-memory, policy from ./policy/trust.yaml
  watching src/ fixtures/  ·  webhooks on :4310

  [10:14:03] envelope  conversation.created  src=fixture
  [10:14:03] dispose   note.write            ALLOW (handler stubbed)
FlagTypeDescription
--policy <file>pathTrust policy to load. Defaults to ./policy/trust.yaml. Swap files to compare dispositions under different policies.
--port <n>numberLocal webhook port for connectors with webhook events. Default 4310. Point the vendor's sandbox, or curl, at it.
replay <path>subcommandPush fixture envelopes through the router. See fixture replay.

Side-effecting handlers are stubbed by default: the validator runs, the policy evaluates, the disposition and receipt are real, and the handler is not called. That default is what makes it safe to iterate on an operator against production-shaped data. Attaching a sandbox connection to un-stub specific tools is covered below.

The local tenant is t_local, with the same tenancy stamping as production: envelopes you emit get tenant_id: 't_local', and an SDK client pointed at the dev server (FIBRIC_BASE_URL=http://localhost:4310) sees only that tenant.

Tailing envelopes

The dev server logs traffic, but the tail commands are the working view: a live, filterable stream of envelopes and dispositions, the local equivalents of the events and receipts APIs.

terminal
# every envelope on the local bus, live
$ fibric events tail
  10:14:03  conversation.created   src=fixture        corr=c-9f21
  10:15:11  conversation.created   src=webhook:4310   corr=c-a044

# filter with the router's own glob grammar
$ fibric events tail --type 'order.*' --source cn-magento

# dispositions, live; the receipt stream is the truth about what would have acted
$ fibric receipts tail
  10:14:03  note.write   ALLOW  ok    key=order-risk:cnv_3021:note
  10:15:11  note.write   DEDUP  ok    key=order-risk:cnv_3021:note
  10:16:40  order.refund BLOCK  -     no matching policy (fail closed)

# one thread of work, end to end
$ fibric receipts tail --correlation c-9f21

The second line of that receipt stream is the one to learn to read: a DEDUP on a replayed envelope means your keys are doing their job. A stream of fresh ALLOWs for what should be one intended effect means they are not.

Fixture replay

Fixtures are JSONL files of recorded envelopes, one per line, in the exact EventEnvelope shape. fibric init seeds one; record more from a sandbox connection or write them by hand.

fixtures/order-burst.jsonl
{"source":"cn-magento","event_type":"order.created","payload":{"entity":{"kind":"order","id":"SO-11290"},"order_id":"SO-11290","total":184.5}}
{"source":"cn-magento","event_type":"order.created","payload":{"entity":{"kind":"order","id":"SO-11291"},"order_id":"SO-11291","total":92.0}}
terminal
$ fibric dev replay fixtures/order-burst.jsonl
  replayed 2 envelopes
  routed   2 -> ship-risk
  proposed 2 plans (3 actions)
  disposed order.hold SO-11290  ALLOW   order.hold SO-11291  ALLOW   notify.send  ALERT
FlagEffectWhat it proves
--propose-onlyPlans are produced and printed; nothing is disposed.Inspect what an operator wants to do before any policy question arises.
--twiceReplays the file, then replays it again in the same process.The second pass must come back all DEDUP. This is the idempotency test to run before every publish.
--strictNon-zero exit on any BLOCK, error, or invalid plan.The CI switch; see the testing recipe.

Hot reload

fibric dev watches src/ and fixtures/, and on change reloads the def and re-registers it without restarting the kernel. What survives a reload is deliberate:

StateAcross hot reloadWhy
Envelope history and receiptskeptYour tails stay meaningful across edits.
Idempotency seen-setkeptSo edit-and-replay behaves like production retries. Restart the server for a clean slate.
In-flight single-flight locksdrained firstA reload never interrupts a disposition mid-action.
Def registrationreplacedTool, event, and trigger changes take effect on the next envelope.
i
A changed def is validated before it replaces the old one

If your edit produces a def that does not compile or validate, the dev server keeps the last good registration running and prints the error. You cannot hot-reload your way into a half-registered connector.

Debugging tools

Invoking tools directly

fibric connectors test exercises one tool with explicit args, no operator and no envelope involved. Side-effecting tools dry-run by default: the validator and the trust evaluation run, the handler does not.

terminal
# a read runs for real
$ fibric connectors test cn-brightdesk conversation.read --args '{"conversation_id":"cnv_3021"}'
  ok  { "id": "cnv_3021", "status": "open" }

# a write dry-runs: validation + policy, no handler call
$ fibric connectors test cn-brightdesk note.write --args '{"conversation_id":"cnv_3021","body":"hi"}'
  input      ok (validated)
  policy     ALLOW (rule: tool=note.write)
  handler    skipped (dry run; pass --execute with a sandbox connection to run it)

Explaining policy decisions

When a disposition surprises you, ask the evaluator to show its work. --explain on a replay prints, per action, which policy rules matched, which constraint failed, and why the result is what it is, in evaluation order:

terminal
$ fibric dev replay fixtures/refund.jsonl --explain
  action order.refund SO-11290 (value: 184.50)
    rule 1  tool=order.hold                 no match (tool)
    rule 2  tool=order.refund maxValue=100  match; constraint FAILED (184.50 > 100)
    result  BLOCK
  receipt written: order.refund BLOCK "blocked by trust policy"

The mechanics under the output are exactly the kernel's: no matching rule is BLOCK, any matching rule with a failed constraint is BLOCK, and an ALERT rule wins over plain ALLOW. If a read of the output does not match your expectation, the policy file is wrong, not the evaluator; that is the point of keeping disposition deterministic.

Two habits round out the toolkit. Structure your ctx.log calls: their output is interleaved into the dev stream with the envelope's correlation id, so a well-logged handler narrates its own trace. And keep a fixtures/regressions.jsonl: every envelope that ever produced a wrong plan goes in it, and --twice --strict over that file becomes your cheapest safety net.

Pointing local code at a sandbox

When stubs are no longer enough, attach a sandbox connection and un-stub deliberately, per tool:

terminal
# a sandbox connection holds sandbox credentials; see connector auth patterns
$ fibric connectors add cn-brightdesk --connection brightdesk-sandbox

# run the dev kernel with real reads and real writes for ONE connection
$ fibric dev --connection brightdesk-sandbox
  cn-brightdesk: handlers LIVE against brightdesk-sandbox (reads and writes)
  all other connectors: stubbed

Sandbox and live credentials are separate connections holding separate secrets, so local work can never silently pick up a production key. The same separation lets you point a locally running SDK client or operator at real sandbox data while every other side effect in the project stays stubbed. Recording fixtures from a sandbox session (fibric events tail --record fixtures/from-sandbox.jsonl) is the honest way to build a replay corpus: production-shaped envelopes with sandbox-safe contents.

Keep going