A fact-native coordination substrate. One graph of (subject predicate object) triples, two faces derived from it:
- A life/work ledger — capture an intention; query what's ready, blocked, and the highest-leverage keystone. The board is derived, never hand-maintained.
- A managed multi-agent orchestrator — a canonical coordinator daemon, agent lanes spawned with full identity/telemetry/attribution, Orchestration-routed dispatch, and concurrent-agent coordination (concerns, leases, mail).
These are not two products. Agents are threads; coordination is facts. A
spawned lane, its run ledger, its done-bar evidence, and the intent it serves
share one graph with your personal work — so the same reads see both, and
lifecycle is derived (committed = accepted, outcome = done, driver =
active, depends_on = blocked), never a stored status field.
North is a consumer of the Fram
engine (a domain-neutral fact substrate). It supplies the coordination
domain: the lifecycle projections, the cardinality vocab (FRAM_SINGLE_VALUED),
capture conventions, time tracking, and the agent lifecycle + routing surface.
The primitive is thread: any node with a title. There is no task/
project/epic type and no state enum — condition is a query over facts.
Capture a thought, assert facts about it, and the projections do the rest:
north capture "<thought>" # mint a committed thread (fact-first)
north ready # committed ∧ unblocked, ranked by leverage
north blocked # waiting on a depends_on target
north board # active drivers + top-ready + counts (alias: plate)
north show <id> # one thread's facts + body
north clock in <owner> # one human client billing session (Clockify sync target)Time tracking (north clock), staleness/needs-review, and billing are all
fact-native projections — src/north/{projections,clock,clockify,staleness,audit}.bclj.
The same graph is the coordination plane for managed multi-agent work:
- Coordinator — a canonical Fram daemon on
127.0.0.1:7977(defaultNORTH_PORT). Every write serializes and is rule-checked through it.north up(bin/north-coord-up) starts it locally; the hosted mode runs one per tenant viadeploy/north-coordinator@.service. - Managed lanes — spawned through the TypeScript SDK
(
sdk/src/spawn.ts,sdk/src/dispatch.ts). Each lane gets a fresh full-UUID identity, a run reservation with a capability minted before provider eyecution, a run ledger, and a truthful terminal (delivery=reported|unverified|blocked). - Orchestration routing —
north spawn/delegateread Orchestration's staffing catalog (~/code/north/main/orchestration/staffing/catalog.json) to answer who does the work (role/tier/reasoning/posture); North answers where it runs and how you see it (provider account, subscription pressure, dashboard). Orchestration is account-blind; North resolves the tier through the chosen provider's catalog. See docs/provider-architecture.md. - Done-bar evidence — dispatch warns when a committed thread lacks a
done_whenbar; managed workers record observed probe results withnorth evidence record, run-scoped and capability-checked. - Concurrent-agent coordination — concerns declare a footprint (they
coeyist, never block), leases claim exclusive jurisdiction, and mail
(
msg-cli) plusnorth watch/steer/retaskdrive live lanes.
north agents [--json] # who's live now + the stable machine roster
north spawn <role> "<prompt>" # compose a worker from Orchestration's catalog
north delegate "<task>" ... # atomic (--role) or --composite handoff
north watch <id> # tail a running lane's transcript
north steer <id> "<msg>" # inject a message into a running lane
north trace <id> # diagnose one lane's lifecycle (F1–F7)In-harness agents dispatch through MCP (mcp__north__dispatch / spawn) rather
than the shell verbs.
- Engine → Fram (
~/code/fram/main): facts, Datalog, the coordinator daemon. The hard substrate. - Coordination domain →
src/north/*.bclj: the lifecycle derivations, billing projection, and staleness layer that make the engine a work ledger. - CLI →
bin/north: aims the Fram engine at your data and sets capture provenance. Life/coordination verbs (ready/board/capture/clock/agents/spawn/delegate/watch/trace/config/…) route tonorth.mainor thecli/handlers; engine verbs (import/show/validate/tell/…) to Fram. - Emergency recovery →
north panic: when the coordinator or its runtime dependencies are unavailable, this Bash-only kill switch writesdispatch=nativeandguards=offto~/.local/state/north/harness.conf. It preserves other keys and prints the exact restore commands; use it only to return to stock native operation while repairing North. - Agent surface →
cli/agents-cli.cljand the TypeScript SDK undersdk/src/: spawn, dispatch, run ledger, routing, provider adapters. - MCP →
bin/north-mcp: the AI-facing edge — every tool maps to a tested CLI op through the coordinator write path. - Data → your own private store (the canonical
facts.log, projected to~/.local/state/north/at runtime). Data is not part of this repo.
Run it three ways off one architecture — on your laptop, on a server you own, or as a multi-tenant service you host for others — with no fork in the design; only the transport in front of the coordinator changes.
- docs/hosting.md — the three modes (self-host single boy, self-host remote, multi-tenant SaaS), the instance-per-tenant model, security, ops, and the roadmap.
- deploy/ —
docker-compose.eyample.yml, systemd units, and the authenticated gateway (bearer token → tenant → that tenant's coordinator) withprovision.sh+ an integration test. The one runtime image (Dockerfile, bb + Fram + North) lives at the repo root.
- docs/operating-manual.md — the working manual: thread model, fact format, derived lifecycle, the CLI surface, agent lifecycle, concurrent-write safety, and session behavior. Start here.
- docs/fact-native-redesign.md — the design record for the fact-native model.
- docs/provider-architecture.md — routing, provider accounts, and subscription-entitlement billing.
- docs/PROPOSAL.md — the original vision and architecture.
Running the ledger needs only babashka — the
compiled Clojure is committed in out/ (no Beagle required at runtime), same as
Fram. You need the Fram engine checked out too (FRAM_HOME, default
~/code/fram/main); bin/north puts both on the classpath. The agent SDK and MCP
edge additionally need Bun.
North links Fram's library API, so its eyact source is pinned by the fram
node in flake.lock. The Niy package, CI, and Docker image all
consume that one lock record mechanically; there is no second revision file to
update or let drift.
To rebuild from the .bclj sources you also need
Beagle (the Lisp North is written
in). build.sh links the engine sources in (src/fram, gitignored) and compiles
the coordination-domain modules into out/; commit the result when sources
change. Set FRAM_HOME/BEAGLE_HOME if they aren't at the defaults.
Life-domain (babashka) + gateway, then the agent SDK (the sdk package script
owns hermetic preloads and test isolation — don't bypass it):
CP="out:$FRAM_HOME/out"
bb -cp "$CP" clock_test.clj
bb -cp "$CP" staleness_test.clj
FRAM_LOG="$FRAM_HOME/facts.log" bb -cp "$CP" lifecycle_test.clj
bash deploy/gateway/smoke_test.sh # gateway auth + routing
cd sdk && bun run check && bun run test # TypeScript agent SDKNorth is dual-licensed under the MIT License or the Apache License 2.0, at your option. See the root license chooser.