5
0
Fork
You've already forked elephant
0
p2p coordination on shared defeasible logic theories
  • Rust 99.5%
  • Shell 0.3%
  • Makefile 0.2%
Hugo O'Connor 13fc8bb317
Add elephant dag: the entire theory as a layered dependency graph
Nodes are literals carrying their effective proof tag (the status
collapse); every rule draws edges from its body literals to its head,
so facts sit at the top and derived conclusions flow downward — the
theory-wide analogue of hence's board --dag, using its Sugiyama-style
renderer (longest-path layering, median crossing reduction, box-drawing
on a TTY with ASCII fallback) made cycle-safe for defeasible theories:
cycle edges are dropped from layout and listed under the graph, as are
rule preferences.
--focus <literal> restricts the view to one literal's cone (transitive
dependencies and dependents), which keeps plan-scale theories readable.
--json emits the raw graph: nodes with layer and tag, edges with rule
label, arrow kind and negation flags (a rule concluding (not p) is an
attack edge into p), superiorities, and cycles.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011fUHP2297xhxrk5GDUVxJU 
2026年07月13日 13:54:48 +10:00
benches M6: fuzz targets, convergence property test, NFR bench; fix BUG-001 2026年07月11日 02:16:51 +10:00
bugs Untrack fuzz corpus/artifacts/target; fix BUG-001 dead link 2026年07月11日 02:18:14 +10:00
docs Continuous P2P sync (REQ-107/108) + two-daemon loopback e2e (BUG-002) 2026年07月11日 19:12:59 +10:00
fuzz Untrack fuzz corpus/artifacts/target; fix BUG-001 dead link 2026年07月11日 02:18:14 +10:00
plans IMPL-001 complete: all 14 tasks done 2026年07月11日 02:21:38 +10:00
scripts Add runnable demo script (Elephant 2000 release-gate + promise story) 2026年07月11日 02:22:48 +10:00
specs SPEC-001 0.2.0: commitment-state reflection (REQ-025..027, ADR-013) 2026年07月12日 13:33:47 +10:00
src Add elephant dag: the entire theory as a layered dependency graph 2026年07月13日 13:54:48 +10:00
tests Add elephant dag: the entire theory as a layered dependency graph 2026年07月13日 13:54:48 +10:00
users CLI: one flat verb surface (ADR-205, supersedes ADR-203) 2026年07月11日 19:13:30 +10:00
vendor p2p: invite codes, SPAKE2 join ceremony, Loro sync (SPEC-002, partial) 2026年07月11日 01:27:10 +10:00
.gitignore CLI: one flat verb surface (ADR-205, supersedes ADR-203) 2026年07月11日 19:13:30 +10:00
Cargo.lock M6: fuzz targets, convergence property test, NFR bench; fix BUG-001 2026年07月11日 02:16:51 +10:00
Cargo.toml M6: fuzz targets, convergence property test, NFR bench; fix BUG-001 2026年07月11日 02:16:51 +10:00
LICENSE Design phase: SPEC-001/002/003, user profiles, concept vault, build skeleton 2026年07月11日 00:25:36 +10:00
Makefile M1: scaffold, identity, envelope, store (IMPL-001 tasks 1-4) 2026年07月11日 00:45:42 +10:00
README.md README: run the daemon under launchd (macOS) 2026年07月13日 10:51:42 +10:00
rust-toolchain.toml Design phase: SPEC-001/002/003, user profiles, concept vault, build skeleton 2026年07月11日 00:25:36 +10:00

elephant

Speech-act coordination on shared, end-to-end-encrypted defeasible theories.

License: Apache-2.0 Stage: v0.1

"I meant what I said, and I said what I meant. An elephant's faithful, one hundred percent! moreover, an elephant never forgets."

What this is

elephant is a CLI + daemon where agents — humans, LLMs, CI bots, webhooks — coordinate by exchanging signed speech acts into shared, append-only logical theories. Whether a task is done, a promise is kept, or a claim stands is derived by defeasible reasoning over signed evidence — never decreed by a status column.

It is the successor to hence: the same task-coordination surface (board, claim/complete/block, why-not/require/what-if), but the plan is no longer a local file — it is a peer-to-peer, encrypted theory that travels.

The concept comes from three observations:

  • Coordination is stigmergic. A Jira column's "one assignee" leaks the moment a linter, a reviewer, an LLM, a webhook, and a human all have opinions on one ticket. Completion should be a conclusion, not a transition.
  • Defeasible logic already handles mixed-trust, mixed-authority claims — facts, rules, defeaters, preferences, trust weights. elephant embeds spindle-rust for the closure.
  • McCarthy already designed the semantics. Elephant 2000 (1989): programs whose I/O is speech acts, that refer directly to the past instead of data structures, and whose correctness is "did it keep its promises?". elephant makes a promise a first-class object whose fulfilment or violation is derived from the corpus.

Quick start

make build # single binary: target/release/elephant
make install # → ~/.local/bin/elephant
elephant id create --name alice # Ed25519 key + did:crdt identity
elephant theory create release # a new encrypted theory
elephant assert 'qa-signed' -t release
elephant assert '(normally r-ready (and qa-signed legal-signed) release-ready)' -t release
elephant status -t release # release-ready: -d (legal-signed missing)
elephant why-not release-ready -t release # rule r-ready: missing legal-signed
elephant assert 'legal-signed' -t release
elephant status -t release # release-ready: +d

Promises (the Elephant 2000 core):

elephant promise 'legal-signed' --by 2026年07月18日T17:00:00Z -t release
elephant commitments -t release # legal-signed ... outstanding
elephant assert 'legal-signed' -t release
elephant commitments -t release # legal-signed ... fulfilled

Usage

Joining across machines

Alice invites; Bob joins with a spoken code. No server, no key exchange:

# Alice
elephant theory invite release
# Share this one-time code: 7842-walnut-harbor
# Bob (code entered on the TTY, never argv/logs)
elephant theory join 7842-walnut-harbor --alias release

Under the hood: the number routes (it derives a pkarr rendezvous record on the BitTorrent Mainline DHT); the two words are the SPAKE2 password (never on the wire). After key confirmation, Alice adds Bob to the theory's MLS group, seals the introduction, hands over the keybook, and Bob pulls the whole (encrypted) history. A wrong code fails opaquely and leaves no partial state.

The task layer (hence-compatible)

elephant theory create sprint --template plan
elephant join-as alice -t sprint
elephant next --agent alice -t sprint
elephant claim models -t sprint
elephant complete models -t sprint # unblocks dependents
elephant board -t sprint

The lifecycle SPL is byte-compatible with hence 0.7's chain-cancellation bundles, so existing hence plans and agent scripts port directly.

The daemon

export ELEPHANT_SYNC_INTERVAL=30 # opt in to continuous P2P sync (seconds)
elephant daemon start # single writer + change watch + sync
elephant status -t release # reflects peers' assertions
elephant watch release-ready -t release # pushed on tag change
elephant daemon stop

The daemon is the local single writer and change-notification plane. With ELEPHANT_SYNC_INTERVAL set, it also runs continuous P2P sync: it serves incoming sync sessions (roster-gated) and dials each theory's roster peers on that interval, so members reconverge without an operator. The sync wire is the cbcl-elephant-sync dialect (typed offer/deliver speech acts over iroh QUIC). Unset or 0 leaves sync off (see Status for why it is opt-in).

One-shot commands route through the daemon when it is live (single writer) and fall back to direct store access when it is not. One identity participates in any number of fully-disjoint theories.

Running under launchd (macOS)

To keep the daemon alive across logins and crashes, run it as a per-user LaunchAgent. Use daemon run (foreground) — launchd supervises the process itself, so the self-backgrounding daemon start would confuse it. Save as ~/Library/LaunchAgents/io.anuna.elephant.plist, with the two absolute paths adjusted (launchd does not expand ~ or environment variables):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>Label</key> <string>io.anuna.elephant</string>
 <key>ProgramArguments</key>
 <array>
 <string>/Users/YOU/.local/bin/elephant</string>
 <string>daemon</string>
 <string>run</string>
 </array>
 <key>EnvironmentVariables</key>
 <dict>
 <key>ELEPHANT_SYNC_INTERVAL</key> <string>30</string>
 </dict>
 <key>RunAtLoad</key> <true/>
 <key>KeepAlive</key> <true/>
 <key>StandardOutPath</key> <string>/Users/YOU/Library/Logs/elephant-daemon.log</string>
 <key>StandardErrorPath</key><string>/Users/YOU/Library/Logs/elephant-daemon.log</string>
</dict>
</plist>
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/io.anuna.elephant.plist
launchctl print gui/$UID/io.anuna.elephant | head # verify it is running
launchctl kickstart -k gui/$UID/io.anuna.elephant # restart (e.g. after reinstall)
launchctl bootout gui/$UID/io.anuna.elephant # stop and unload

Notes:

  • With KeepAlive, launchd restarts the daemon if it exits — including after elephant daemon stop. Use launchctl bootout to actually stop it.
  • The daemon's flock means a launchd-managed daemon and a manual elephant daemon start cannot run at once; the second refuses with daemon already running (pid ...).
  • Add a RUST_LOG key (e.g. elephant=debug) to EnvironmentVariables for sync-session tracing in the log file.

Architecture

Four sibling libraries do the load-bearing work; elephant is the glue.

 ┌────────────── elephant ───────────────────┐
 │ CLI (clap) daemon (loopback API) │
 │ │ │ │
 │ ▼ ▼ │
 │ ┌───────────────────────────────────┐ │
 │ │ theory store — one LoroDoc/theory │ │
 │ │ sealed append-only corpus (MLS) │ │
 │ └───────────────┬───────────────────┘ │
 │ │ verify → open → close │
 │ ┌───────────────▼───────────────────┐ │
 │ │ PURE CORE closure(corpus,trust,t)│ │
 │ │ envelope · tombstone · commitments│ │
 │ └──┬────────┬────────┬────────┬─────┘ │
 └─────┼────────┼────────┼────────┼───────────┘
 ┌────▼──┐ ┌───▼────┐ ┌─▼──────┐ ┌▼───────┐
 │cbcl-rs│ │spindle-│ │did-crdt│ │ openmls│
 │dialect│ │rust SPL│ │identity│ │ MLS │
 └───────┘ └────────┘ └────────┘ └────────┘
 transport: iroh QUIC + pkarr/Mainline-DHT · join: SPAKE2
  • cbcl-rs — the cbcl-elephant speech-act dialect (assert, retract, query, concede, commit, request, justify), canonical bytes, Lean-verified R1–R4 invariants.
  • spindle-rust — defeasible closure, trust weighting, explain/why-not/require/what-if.
  • did-crdt did:crdt identity (pure core, no networking).
  • loro — the replicated append-only corpus.
  • openmls — one MLS group per theory; corpus entries sealed under keybook data keys; removal rotates the key.

The pure core (src/core/*) is a deterministic function of its arguments — closure takes the evaluation time as a parameter, never a syscall — which is what makes convergence (identical conclusions on every replica, in any merge order) a property test rather than a hope. See the specs for the full design:

Development

make check # cargo test + fmt --check + clippy -D warnings
make test # cargo test
make bench # criterion NFR benchmarks (closure latency)
cargo +nightly fuzz run invite_code # any of: entry_json spl_payload cbcl_wire invite_code sealed_entry
zetl check --fail-on error # spec-vault traceability (this repo is a zetl vault)

Requires a Rust toolchain ≥ 1.85 (spindle-core is edition 2024). The four Anuna sibling repos are consumed as path dependencies (../cbcl-rs, ../spindle-rust, ../did-crdt); clone them alongside this one.

Status

v0.1. The core (identity, corpus, closure, commitments, queries), the hence-successor task layer, the daemon's loopback control plane, MLS end-to-end encryption (including member removal with corpus-key rotation), and the SPAKE2 join ceremony are implemented and tested (the join choreography and the E2EE removal property are covered end-to-end). The iroh-QUIC transport primitives are wired — durable/rendezvous endpoints, DHT discovery, and a framed sync session — and the sync session converges two replicas in tests over an in-memory duplex; the live loopback dial is #[ignore]d (needs endpoint discovery to settle).

Continuous P2P sync — implemented, opt-in. With ELEPHANT_SYNC_INTERVAL set, the daemon runs a roster-gated accept loop (REQ-107) and a periodic roster-peer dialer, exchanging Loro deltas as cbcl-elephant-sync speech acts; two replicas reconverge to equal version vectors. The dialect, roster gate, sync session, and grow-only import are covered by unit and duplex tests. It is opt-in rather than on-by-default because the live iroh-QUIC dial cannot be exercised in CI (its loopback test is #[ignore]d pending endpoint discovery); the composed pieces are proven over an in-memory duplex.

Not yet: corpus encryption is symmetric-at-rest per member (no storage-level adversary model), and the SPAKE2∘MLS∘keybook composition carries an inherited Tier-1 crypto-review open item from SPEC-047. LLM-orchestration features from hence (agent spawn/watch, plan translate/decompose) are deliberately deferred. See each spec's Orientation → Open section.

References

  • McCarthy, J. Elephant 2000: A Programming Language Based on Speech Acts. Stanford, 1989. https://www-formal.stanford.edu/jmc/elephant.pdf
  • Nute, D. Defeasible Logic. (the SPINdle lineage spindle-rust implements)
  • O'Connor, H. CBCL: Safe Self-Extending Agent Communication. LangSec 2026.
  • RFC 9420 (MLS), RFC 9382 (SPAKE2), BEP-44 (Mainline DHT mutable items).

License

Apache-2.0.