Rust in Peace
0 / 51 viewedOpen any slide to read it — each one earns a green check, kept only in this browser and never sent anywhere. View them all to unlock your certificate.
Chapter 1
Intro
Agenda
An agenda, not a lecture outline — you are here to argue back. By the end of the half hour you will be able to:
- Recount how a two-person team crossed from software to law without dropping the toolchain. Trace our process from the law, to a Cucumber feature, to a reusable template, to the signed notation it produces for one client. Dissect one workflow, forming a Nevada LLC, into attorney-gated steps with the shipped code. Map the Rust ecosystem we rely on and the seams that let us change it. Defend the claim that a reviewed, repeatable workflow beats a prompt. See how a grounded issue becomes a reviewable change, with automation carrying evidence forward and a person retaining the decision.
Las Vegas, 2011 — where I learned to program
Before the law, before Rust, there was a Ruby on Rails meetup in Las Vegas. That room is where I learned to program — and where I first watched a group of strangers agree, on purpose, about how software should be built.
Chapter 2
The Rails Lesson
Convention over configuration
Rails' core idea was radical to a beginner: stop configuring, start agreeing. If everyone follows the same convention, the framework fills in the rest — and a stranger can read your code.
The database was a shared design
The first convention I learned was the schema. Migrations turned the database into something a team designed together — versioned in the repository, applied the same way on every machine.
Controllers — where a request becomes a decision
The next thing we agreed on was the controller: the one place a request turns into an action. Same file, same shape, in every app — so anyone could find the logic.
Parameter handling — the shape of what comes in
Then we learned to distrust the input. Parameter handling was the convention for taking what a user sent and making it safe before it touched anything that mattered.
Middleware — the layer I didn't know I needed
The last convention floored me: middleware. A stack of small, composable layers every request passes through — logging, auth, sessions — each one modular, each one reusable.
"It works on my machine"
And then there was the phrase every one of us said, half-joking, in that Rails room: "It works on my machine." It was a promise and a curse — the first bug we could never reproduce.
How new all of this still was
What stunned me most was how young it all was. These were not settled laws handed down — they were conventions a small community was inventing, out loud, in real time. And I got to be in the room.
From Rails conventions to Navigator
Fifteen years later I am a lawyer, and I kept every one of those lessons. Schema, controllers, parameters, middleware, convention over configuration — Navigator is those Rails ideas, applied to the practice of law in Rust.
A eulogy for my programming career
I thought becoming a full-time lawyer meant leaving production software behind. Rust changed the obituary: I can let the old career rest because its best habits now run the law practice.
Chapter 3
Shared craft compounds
Keep domain seams in Rust, where interest compounds
The bet is not "write a service in Rust." Rust owns reusable domain systems: libraries, command-line tools, workers, editor integrations, tests, and — through Dioxus — the browser pages. The payoff is that each good decision reinforces the next one.
Widely available — governed in the open
Rust is stewarded by the Rust Foundation, an independent non-profit whose members include AWS, Google, Microsoft, and Meta — none of whom own the language. Wide availability is the access-to-justice argument: the toolchain costs a clinic exactly what it costs us — nothing.
A cautionary tale — Java, Oracle, and the price of a single owner
Java was created at Sun; Oracle acquired Sun in 2010 and sued Google that same year over Android's reuse of 37 Java SE API packages. Google v. Oracle ran more than a decade, until the Supreme Court ruled in 2021 it was fair use.
Say it with your whole chest
And when someone asks why it is written in Rust: because we are doing it in motherfucking Rust.
Chapter 4
Six ways to know a change holds
One giant change deserves more than one glance
When two numbers are too large to hold side by side, you can compare several well-chosen positions instead of trusting one glance. AI-assisted code is similar: no one review sees the whole change, so we ask the same question six different ways.
The compiler asks whether the program can be true
Rust compiler: do the types, ownership, lifetimes, and exhaustive cases still compose into a program the machine can run?
Clippy and rustfmt ask whether the code speaks our dialect
Clippy finds suspicious choices. rustfmt removes accidental visual difference. Together they make the code easier for the next reader — and the next agent — to recognize.
Tests ask whether the promise still holds at the boundary
Tests are the behavioral measurement: given this input, state, or failure, does the system still keep the promise it made before the refactor?
Bloom turns verification into a learning loop
The Bloom taxonomy gives the same work six human verbs: remember, understand, apply, analyze, evaluate, create.
Marketing copy must explain the way, not only the claim
“AI helps us move faster” is not an explanation. The useful promise is: we move faster because every change meets several explicit checks before it earns trust.
Three workshops, three people, one method
The same explanation changes with the person doing the work:
- Using the Navigator — the licensed lawyer checks a matter and signs the result.
- Operating Navigator — the admin operator checks the environment before it goes live.
- Contributing to Navigator — the contributor checks that an improvement protects the shared corpus.
Fast generation, slow enough verification
Vibe code quickly. Then keep it DRY, refactorable, covered, formatted, and explainable — six perspectives make the feedback loop faster, not heavier.
Chapter 5
From Law to Workflow
The goal — deterministic workflows from law
The whole method on one slide: a prompt is a wish; a workflow is a contract. Read the law → a Cucumber feature → a template (the reusable blueprint) → a notation (one client's reviewed, signed result).
Step 1 — read the law
A Nevada LLC is a creature of statute: NRS Chapter 86 says what the Articles of Organization must contain. We do not paraphrase from memory — we read the chapter at its official source and cite it.
Step 2 — write the behavior before the code
The first artifact is not Rust — it is a Cucumber feature describing the whole arc in plain language, runnable as a test: a founder intakes, an attorney reviews, signatures land, and the state stamps a filing.
From features/tests/features/nest_formation.feature:
Scenario: From intake to a stamped Secretary-of-State filing
When the firm opens the "nv__llc_formation" matter for the client
And the founder answers the formation questionnaire:
| value |
| Libra |
| Bright Star Ventures |
| Neon Law Registered Agent |
| members |
| Libra; 1 Main St; Las Vegas; NV; 89101; USA |
| 2026-07-01 |
And the attorney approves and sends the document
Then the formation reaches the signature wait
And the persisted packet is the official SoS form carrying the founder's answers
And the generated packet is filed as a document in the matter
When the attorney files the Articles with the Nevada Secretary of State
Then the formation workflow reaches END
And a filing was recorded with the "Nevada Secretary of State"
And the founder's six onboarding answers are on file
"It works on my machine" — so we stopped trusting my machine
Callback. The fix for that old curse is not a better machine — it is to stop trusting any machine. The scenario above runs against a real, throwaway Postgres, so the environment is pinned in code, not remembered on a box.
Step 3 — the template: a questionnaire and a workflow
The template is one markdown file with two machine-readable graphs: a questionnaire graph (what we ask) and a workflow graph (what we do). The Nest questionnaire is six answers, in order.
From templates/forms/united_states/nevada/state/nv__llc_formation.md:
questionnaire:
BEGIN:
_: person__client
person__client:
_: entity__company
entity__company:
_: person__registered_agent
person__registered_agent:
_: custom_single_choice__management_structure
custom_single_choice__management_structure:
_: people__managing_members
people__managing_members:
_: custom_datetime__formation_date
custom_datetime__formation_date:
_: END
END: {}
And here is the workflow — the LLC formation dissected into small, named, modular steps. Each state is a noun in our glossary; each transition is a signal some handler fires. This graph is the product.
From templates/forms/united_states/nevada/state/nv__llc_formation.md:
workflow:
BEGIN:
intake_submitted: intake_persisted__organizer
intake_persisted__organizer:
articles_rendered: staff_review
staff_review:
approved: generate_pdf__articles_pdf
rejected: END
generate_pdf__articles_pdf:
pdf_persisted: sent_for_signature__pending
sent_for_signature__pending:
signature_received: filing__nv_sos
signature_declined: END
filing__nv_sos:
filed: END
END: {}
Step 4 — the attorney gate is a graph invariant
Every workflow must pass through staff_review before anything is signed or filed — not as a policy memo, but as a
property checked over the state-machine graph itself with a breadth-first search from BEGIN.
From workflows/src/guardrail.rs:
pub fn staff_review_precedes_signature(spec: &WorkflowSpec) -> Result<(), GateViolation> {
let begin = StateName::begin();
if let Some(signature) = reaches_target_without_review(spec, &begin, is_signature_state) {
return Err(GateViolation {
fill_state: begin.as_str().to_string(),
submission_state: signature,
});
}
Ok(())
}
Step 5 — signature is a modular step
sent_for_signature__pending is one state in the graph, and the thing that fires it is a small trait — not a vendor.
DocuSign is the shipped implementation; dev and tests run a recording stub, so the step stays testable without an
account.
From portal/src/signature.rs:
pub trait SignatureProvider: Send + Sync {
/// Submit the rendered retainer PDF for the given notation, placing
/// the fields described by `manifest`. Returns a provider-issued id
/// correlating future events.
async fn send_for_signature(
&self,
notation_id: Uuid,
pdf: &[u8],
manifest: &SignatureManifest,
) -> Result<SignatureRequestId, SignatureError>;
Because the step is modular it can also be careful. Dispatch is idempotent — a notation that already has an envelope out reuses it, fires nothing, and sends nothing — so a retry can never double-send a client's contract.
From portal/src/retainer_walk.rs:
// Idempotency: this notation already has an envelope out. Reuse the
// recorded id, fire nothing, send nothing — the post-state is
// whatever the notation already records.
if let Some(existing) =
store::signatures::request_id_for_notation(deps.surreal, notation_id).await?
{
return Ok((
StateName::from(notation_row.state.as_str()),
crate::signature::SignatureRequestId(existing),
));
}
Step 6 — the filing, run durably
The last state, filing__nv_sos, records the filing with the Nevada Secretary of State — and like every long-running
step it executes as a journaled, resumable Restate workflow through the
restate-sdk crate.
"It works on my machine" — even when my machine dies
Restate takes the phrase somewhere new. The durable filing does not live on the machine at all — it is journaled, so it survives the pod that ran it and replays exactly where it left off.
Why a workflow beats a prompt
You could ask a frontier model to "form me a Nevada LLC" and get something plausible. We built the harness instead, because plausible is not the bar — repeatable is. A prompt's steps are neither repeatable nor modular.
Privacy-preserving operations are part of the product
Legal tech cannot treat observability as a copy of production. Navigator emits OpenTelemetry traces, metrics, and logs through one Rust crate, and the rule is structural: identifiers and counts, never client content.
Chapter 6
Rust All the Way Down
We did not start all Rust
Navigator did not begin as a pure-Rust stack. We have moved the boundary a seam at a time: server-rendered Maud with HTMX/Alpine/Bootstrap gave way to Dioxus; React and TipTap were evaluated and declined; the browser now shares the Cargo workspace with the rules and the server.
From Postgres to Rust — with a bridge
Postgres and SeaORM still hold the production record today. SurrealDB is the chosen destination: document, graph, and key-value work can meet in one Rust engine, while each port slice proves its reads and writes before the old path goes away.
The runtime story: keep the seam, change the engine
The same move repeats at the runtime boundary. Durable workflows landed on Restate, local identity on Rauthy, the private edge on Pingora, local S3 on Garage, and observability on OpenObserve — each behind an application-owned protocol, trait, or environment contract.
One deployment bucket; many matter repositories
Storage and Git deliberately have different cardinality. The target is one private object-storage bucket per
deployment; documents, assets, exports, logs, and every Project's files use application-owned logical keys and
metadata inside it. Git is the inverse: one deployment-specific GitHub organization holds many private repositories,
such as fiat-law-staging-projects/PROJECT_CODE_1 and fiat-law-staging-projects/PROJECT_CODE_2.
Rust where we can own the path; partners at the edge
The destination is not an ideological ban on vendors. Surreal Cloud, Restate Cloud, OpenObserve Cloud, Stalwart Managed Email, and Miuda PBX are the Rust-operated lanes under recorded cutover gates. Xero, DocuSign, and the SIP carrier stay at the business edge, where they provide the service we do not claim to reimplement.
Rust reaches the places lawyers work
Navigator is not only a web app. The same rules meet lawyers in the document folder they already understand, and the same AIDA catalog is available to assistants through MCP and A2A.
I want a commercial relationship with everything I depend on
A small legal team cannot maintain the world's infrastructure — so I want to pay for it, in the open. A vendor like Restate, or GitHub Sponsors for the libraries. A healthy dependency is one someone is paid to keep healthy.
Always the latest, always Rust
Two rules keep the interest compounding: take the newest version of every dependency, and use exactly one language. No pinned-and-forgotten crates, no second runtime, no polyglot seams to babysit.
Swap the seam — it still works
Every heavy dependency sits behind a trait, so changing one does not make the application a rewrite. Signatures, storage, identity, durable execution, policy, telemetry, and the project repository surface have all had an explicit seam. Sometimes the right change is a new provider; sometimes it is retiring a surface entirely.
The crates we actually run on
The bill of materials for a real legal-tech product — every line a crate you can pull today:
- HTTP and views —
axum,dioxus,tower/tower-http. - Async runtime —
tokio, multi-threaded, with graceful shutdown. - Database transition — Postgres today; SurrealDB follows through the next store slices.
- Durable execution —
restate-sdk. - Telemetry —
opentelemetry,tracing, OTLP. - Archive —
arrowandparquet; a real Iceberg table lane is separately tracked. - Content —
pulldown-cmark. - Cloud —
google-cloud-storage+reqwest. - Identity —
jsonwebtoken+oauth2. - Tests —
fantocciniandcucumber, exercised through the workspace harness.
Ethics is part of the stack
Lawyers who code still carry the rules of professional conduct, and the engineering answer is the same as it is for memory safety: make the invariant structural, not aspirational.
- Scope is a field, not a vibe. Every engagement is scoped in writing before work starts. The conflict check runs first. Before any matter opens, we query every current and former matter. Referral, without a referral fee. When conflicted out, we refer — with no referral fee.
The simplest developer environment I can get away with
The local loop is production-shaped on purpose: the Rust CLI provisions an isolated KIND dependency tier and the host application process shares that exact environment.
The online developer environment
So we built the environment in the cloud too. The same Rust processes, the same swappable seams, spun up online — so the loop follows me anywhere, and I barely write the code by hand at all. Agents do the typing; I review.
"It works on my machine in the cloud — and on yours, and on my client's"
The phrase, finally earned. It works on my machine in the cloud. It works on your machine. It works on my client's machine — because the environment is swappable seams, pinned services, and durable runs, not a special box.
Where we are today — live by August 19
Every seam now has its Rust answer, decided and recorded: SurrealDB (Surreal Cloud) for the store, Restate Cloud for durable execution, OpenObserve Cloud for telemetry, Stalwart Managed Email for every matter address, Miuda PBX on RustPBX for voice, Pingora at the private edge, Garage and Rauthy in the local loop, Regorus for policy, Dioxus for every view, Typst for every document. Running by August 19, 2026.
Betting on ourselves — Rust in peace
Choosing these vendors is betting on ourselves. If we write successful Rust, our vendors write successful Rust — a healthy ecosystem that stays usable for many years. That lets the firm focus on winning cases, and lets me Rust in peace: no longer a full-time developer, standing on the shoulders of giants.
Chapter 7
Wrap Up
Take the method home
The ask is not a star on a repository — it is the method, and every piece of it is a crate you can pull tonight. Read the law. Write the behavior first. Keep the domain seam in Rust. Put a human at the gate.
Read the Foundation mission for why any of this matters — then come find me afterward and let us get crabby together.
You finished — claim your certificate
Enter your name and email and the Neon Law Foundation will send a PDF certificate of completion.
We use your email only to send this certificate.