Developer Guide
This guide is for engineers building new services (apps) for the nara runtime or contributing to the core system.
1. Purpose
Section titled “1. Purpose”- Define the developer experience (DX) and vision for the nara runtime.
- Provide a step-by-step path for creating and testing new services.
- Document the build system, environment configurations, and testing strategies.
2. Conceptual Model
Section titled “2. Conceptual Model”The Vision: Nara as an Operating System
Section titled “The Vision: Nara as an Operating System”Nara is not just a node; it is a runtime or “Operating System” for distributed agents. Services are programs that run on it. The runtime provides the “hardware” and “drivers”:
- Identity: Cryptographic keys and signature authority.
- Memory: The
Ledgerfor event storage and retrieval. - Transport: Abstracted MQTT (Plaza) and Mesh (HTTP) communication.
- Security: Self-encryption primitives (
Seal/Open).
Everything is a Message
Section titled “Everything is a Message”In the nara runtime, every fact, request, or local notification is a Message. Whether it’s a persistent event stored in the ledger or an ephemeral P2P ping, it uses the same envelope. The difference is defined by Behavior.
Behavior-Driven Development (BDD)
Section titled “Behavior-Driven Development (BDD)”Instead of writing complex protocol logic, developers declare behavior. You define:
- The Kind: A unique string identifier (e.g.,
stash:store). - The Pipeline: Which stages (Sign, Store, Gossip, Filter) the message passes through.
- The Handler: The function that executes when a message of this kind is received.
Invariants
Section titled “Invariants”- Services MUST NOT store state on disk; use the
Stashservice or theLedgervia the runtime. - Services MUST be transport-agnostic; use
Runtime.EmitandRuntime.Receive. - Handlers MUST be idempotent and handle out-of-order messages where possible.
3. External Behavior
Section titled “3. External Behavior”Developer Tools
Section titled “Developer Tools”- The CLI:
-verbose: Enables debug logging across all services.-nara-id <name>: Overrides identity for testing multiple instances locally.-serve-ui: Launches the embedded web inspector.
- MockRuntime: A high-fidelity test double that allows you to “run” a service in a unit test, capturing its emissions and simulating network input without a real network.
Environment Modes
Section titled “Environment Modes”EnvProduction: Default. Graceful error handling, focused logging.EnvDevelopment: Warnings for suspicious behavior, detailed logs.EnvTest: Strict. Panics on pipeline failures to catch bugs early in the CI/CD cycle.
4. Interfaces
Section titled “4. Interfaces”The Service Interface
Section titled “The Service Interface”All nara “apps” must implement the Service interface.
type Service interface { Name() string Init(rt RuntimeInterface) error // Store the runtime reference Start() error // Start background loops/goroutines Stop() error // Graceful cleanup}The BehaviorRegistrar
Section titled “The BehaviorRegistrar”Most services also implement this to tell the runtime which messages they handle.
type BehaviorRegistrar interface { RegisterBehaviors(rt RuntimeInterface)}Build & Test Commands
Section titled “Build & Test Commands”Always use /usr/bin/make to ensure consistent toolchains:
/usr/bin/make build: Compiles the binary tobin/nara./usr/bin/make test: Runs the full integration suite (3+ minutes)./usr/bin/make test-fast: Runs unit tests (-shortflag).
5. Message Schemas
Section titled “5. Message Schemas”Payloads are Go structs that the runtime automatically serializes to/from JSON.
type MyPayload struct { Target types.NaraID `json:"target"` Content string `json:"content"`}6. Algorithms
Section titled “6. Algorithms”Creating a New Service
Section titled “Creating a New Service”- Define the Payload: Create the struct for your data.
- Implement Service: Create your struct with
Init,Start,Stop. - Declare Behaviors: Use the runtime templates (e.g.,
StoredEvent,Ephemeral,MeshRequest) to register your kinds. - Wire the Handlers: Map your message kinds to typed handler functions.
- Add to Runtime: Register the service in
runtime_integration.go.
Case Study: The Stash Service
Section titled “Case Study: The Stash Service”The stash service is the reference implementation for the Nara runtime. It provides distributed encrypted storage by using Nara as its operating system.
1. Defining Behaviors
Section titled “1. Defining Behaviors”The stash service implements the BehaviorRegistrar interface. It defines how its messages move through the network:
func (s *StashService) RegisterBehaviors(rt RuntimeInterface) { // stash:store is a mesh-only request (no MQTT broadcast) rt.Register(MeshRequest("stash:store", "Store encrypted stash"). WithPayload[StashStorePayload](). WithHandler(1, s.handleStoreV1))}2. Version-Aware Handlers
Section titled “2. Version-Aware Handlers”Stash handles schema evolution by mapping versions to specific handler functions. The runtime automatically deserializes the correct payload type:
func (s *StashService) handleStoreV1(msg *Message, p *StashStorePayload) { s.log.Info("received stash store request", "owner", p.OwnerID) // Business logic goes here...}3. Using Runtime Primitives
Section titled “3. Using Runtime Primitives”Instead of managing its own crypto or transport, Stash consumes Nara OS primitives:
- Logging:
s.log = rt.Log("stash")(Scoped, structured logs). - Encryption:
rt.Seal(data)(Uses the Nara’s hardware/identity keys). - Transport:
rt.Emit(msg)(Transport-agnostic delivery).
The Pipeline Flow
Section titled “The Pipeline Flow”graph TD
A[Service: rt.Emit] --> B{Behavior?}
B --> C[ID Stage]
C --> D[Sign Stage]
D --> E[Store Stage]
E --> F[Gossip Stage]
F --> G[Transport Stage]
G --> H[Notify Stage]
7. Failure Modes
Section titled “7. Failure Modes”- Orphaned Messages: Emitting a message without a registered Behavior returns an error.
- Signature Mismatch: If a peer sends a message with an invalid signature, the
VerifyStagein the receive pipeline drops it before it hits your handler. - Schema Drift: If
MinVersionandCurrentVersionare not managed, older nodes will drop messages they cannot deserialize.
8. Security / Trust Model
Section titled “8. Security / Trust Model”- Isolation: Services cannot access each other’s private data unless explicitly exposed via a message kind.
- Attestation: Use
rt.LookupPublicKey(id)to ensure you are talking to the real owner of a Nara ID. - Sovereignty: Users own their data through
Seal/Open; the runtime provides the crypto, but the service defines the policy.
9. Test Oracle
Section titled “9. Test Oracle”- Verification: Use
rt.EmittedMessagesin tests to verify your service reacted correctly to an input. - Determinism: Given the same input messages, a service should reach the same internal state every time.
10. Open Questions / TODO
Section titled “10. Open Questions / TODO”- Dynamic Loading: Can services be loaded as WASM modules in the future?
- Resource Accounting: Limiting CPU/Memory usage per service.