Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This book is about how ikigai is put together, and about one claim in particular: ikigai does not have a user-experience layer and a security layer that were designed separately and bolted together. It has one substrate, and the patterns a person operates are the same patterns that authorize and confine them. The grant that a consent screen renders is the grant the kernel checks. The action list a page draws is the action list an agent is handed. The cache that makes a second read cheap is the boundary that keeps one person’s answer from reaching another. Each of these is one mechanism with two faces, and this book teaches the mechanism once and then turns it to the light twice.

Before any of that, though, it is worth saying plainly what kind of system ikigai is, because the shape of everything after follows from it.

Resource-oriented computing

ikigai is a resource-oriented system. Everything it deals with — a file, a calculation, a ledger item, a capability, the catalog of what the kernel can do — is a resource: a value with a name, reached the same way whatever it is. You do not call a function that returns a ledger item; you resolve the name of the item and get back a representation of it. You do not open a connection and send a command; you ask one of a small, fixed set of verbs of a name, and the kernel routes the request to whatever is bound to answer for it.

That idea is not new, and this book does not pretend it is. Resource-oriented computing was worked out in detail in NetKernel, designed by Peter Rodgers and Tony Butterfield, and ikigai owes it the core of its thinking. You do not need to know NetKernel to read this book — everything is taught here from the ground — but it is honest to name where the ideas come from.

What does a reader get from a system built this way? A few things, and they compound:

  • One uniform interface. There are five verbs and no more: Source (read), Sink (write), Exists, Delete, and Meta (describe yourself). Every resource answers the same five, so there is one thing to learn, not one API per subsystem.
  • Composition by default. A resource can be built from other resources by name. A page is a template filled with the names it needs; a ledger’s ranked view is a query over the ledger’s graph. Nothing special connects them — they are all just names the kernel resolves.
  • Determinism and cheap repetition. When a resource is a pure function of its inputs, the kernel can cache it, and — because it tracks what each answer was made from — it can know exactly when the cached answer has gone stale and no sooner. Reading the same thing twice is nearly free, and that is not an optimization bolted on top; it is a property of naming things honestly.
  • Machine-legibility. Every endpoint describes itself, and the kernel publishes a catalog of what it can do and an action list scoped to who is asking. A program — or an agent — can read the system’s own description of itself rather than being told about it out of band.

That last point is why ikigai keeps turning up in the same conversations as agentic AI. An agent needs to know what actions are available, do them safely, and cite where its answers came from. A resource-oriented kernel offers exactly those as first-class facts: the action list is the tool manifold, capabilities are the safety model, and provenance is the citation. The substrate an agent needs and the substrate a careful application needs turn out to be the same substrate.

The Web is resource-oriented computing with a fixed vocabulary

The quickest way to understand ikigai is to notice that you already use a resource-oriented system every day. The Web is one. It names things (URLs), it has a small fixed set of verbs (GET, POST, PUT, DELETE), it negotiates representations (an Accept header asks for HTML or JSON of the same resource), and it caches by URL (with ETag and Cache-Control). Those are the same four ideas — naming, verbs, representation, caching — that this book is about.

The Web made each of those choices in one particular way, and froze it. ikigai takes each axis and generalizes it:

axisthe Webikigai
naminghttp(s) URLsany urn: scheme — a file, a function, a ledger item, a capability, the catalog itself all have names in the same space
verbsfour, and not uniform (much sits behind POST)five — Source, Sink, Exists, Delete, Meta — over every resource, uniformly
representationcontent negotiation on a mostly-fixed set of typestransreption: any representation converts to any other through converters, with one canonical hub (Turtle), so a new face is a converter, not a new endpoint
cachingby URL, with time-to-live and validatorsgolden threads: the kernel tracks what each answer depended on and invalidates exactly those, so a write cuts precisely the derived answers it should and no others
authoritybolted on per application (cookies, sessions, OAuth)capabilities: authority is a resource too, attenuated down every call, and the kernel’s capability check is the only authorization

None of this makes the Web wrong. The Web’s fixed vocabulary is why it scaled to the planet. The point is that its shape is one instance of a more general shape, and once you see the general shape you can apply it where the Web’s particular freezing of it gets in the way: to a calculation, to a private tenant’s data, to an agent’s tool list, to a cache that must respect who is asking.

A note on RDF and XSLT

ikigai leans on RDF (a graph data model) and, in places, XSLT (a transform language for turning one representation into another). A reader coming from a mainstream background may read that as a cost of entry. It is worth being clear that it is a choice with a reason, not a tax.

The reason is that the data in this system already is a graph. An endpoint’s self-description, the catalog of what the kernel can do, the provenance trail behind an answer, a ledger item and its comments and links — these are nodes and edges whether or not you write them down that way. RDF is how you write down a graph without inventing a bespoke shape for each one, and it is why two different subsystems’ descriptions can be read by one tool. XSLT earns its place for the same reason the Web uses it: turning a graph into a page is a transform from one representation to another, which is exactly what a transreptor is.

And it is not a requirement for using the system. A view can be composed from a JSON document or a CSV table just as well as from a graph; the plain-text face of a resource is the default, and a JSON face is a converter away. RDF is the hub because a hub has to be something, and a self-describing graph is the honest something for a system whose data is already shaped like one. Where this book shows a graph face, it says so, and it does not ask you to reach for RDF where a simpler representation is the natural one.

How this book is organized, and what it assumes

This is the fourth book in the library. It assumes you have met the core ideas — resources, the five verbs, transreption, golden threads, capabilities — in The ikigai Book, and it names them as it builds on them rather than teaching them again. It is shorter than the others on purpose: it is a tour of the architecture for a reader who can already write against the kernel and now wants to see how the pieces fit, and why the pieces that confine are the same pieces that serve.

It carries one worked resource throughout: a ledger item, the kind of thing The gonk Book builds a whole host around. An item is a good choice because it is already everything at once — a page, a name the kernel resolves, a set of actions gated by capabilities, and a record with provenance. Each chapter turns that one resource to the light from a new angle.

Where a demonstration can run, it runs. This book’s pages carry runnable cells: the same work ledger The gonk Book teaches, compiled to WebAssembly and running in your browser. When you press Run, a real kernel files the item, ranks it, and refuses what your capability does not allow — in the page, for real, nothing recorded. A few patterns in the later chapters cannot run in a page yet (they need parts of the system a browser has no sockets for); those are drawn as pictures, and the text says plainly which is which and promises the runnable version as the tooling for it lands.

The chapters, in order:

  1. One substrate, two faces — the thesis, on the worked item.
  2. The capability spine — authority resolved once per request, and the user interface that falls out of it.
  3. The door — the one place where the spine meets a browser.
  4. The overlay chain — the governors that wrap a resource to make it resilient and bounded.
  5. The economics — golden threads and caching, and why they are the system’s accounting.
  6. Closing the loop — the human-in-the-loop action and the record it leaves.

What’s new

What has changed in The ikigai Architecture, most recent first: the date, what was added or changed, and the pages to read for it. Smaller upkeep (moving to a newer release when nothing a reader does changes, a corrected link, a typo) is not listed.

2026-10

  • 2026-10-30: The first section of “Resolve the viewer’s authority once” is now “Capabilities: a resource that only narrows”, in the same form as the sections after it.

    Pages: Resolve the viewer’s authority once

  • 2026-10-28: “One module, a boundary and a surface” has a new section, “Bodies larger than memory”: how the HTTP door of ikigai-cli 0.2.2 frames a streamed answer (Content-Length or chunked), serves byte ranges (206, 416, If-Range), spills uploads to disk under its 408 and 413 bounds, and why every ETag changed once.

    Pages: One module, a boundary and a surface

  • 2026-10-27: The book now builds against ikigai-core 0.2, the streams release. In “Spaces that wrap spaces”, the trace of the state board no longer ends with defaulted=as: the REPL’s plain trace leaves the kernel’s defaulted inputs out now, and trace --defaults shows them. In “The manifold, and the UI that falls out of it”, the ledger’s doctor action is listed in the place the kernel now lists it, under both capabilities.

    Pages: Spaces that wrap spaces, The manifold, and the UI that falls out of it

  • 2026-10-27: “The other books” now links Running ikigai, the operator’s side of the door and capabilities this book describes, and says the library lists all five books.

    Pages: The other books

  • 2026-10-26: In “Spaces that wrap spaces”, “Compare with” is now one list, above Before and After, instead of a second row of a dozen buttons under the tabs that was hard to tell from them on a phone. It offers the stages before the open tab, a stage your buttons made among them, and starts at the stage just before; opening a tab puts it back there. When a tab’s step has moved nothing yet, After now says so plainly: the stage your kernel is still at, and that the tab’s step has moved nothing yet.

    Pages: Spaces that wrap spaces

  • 2026-10-26: The stages of “Spaces that wrap spaces” now hold still: below the tabs, the open stage’s picture sits beside the stage before it, Before and After, with what changed from one to the other in words. “Compare with” puts any earlier stage in Before, and a stage a button makes is shown beside the stage the harness was at before the press. Each picture is the one your kernel drew if that tab’s step ran, or the chapter’s own, said so, in a dashed frame. On a phone the two stack; long answers scroll sideways instead of wrapping. The arrow keys on the tabs now move between tabs only; they used to open the next or previous chapter as well.

    Pages: Spaces that wrap spaces

  • 2026-10-25: The stages of “Spaces that wrap spaces” now work as one box: each tab holds its step and its answers, scrolling in place, and one picture of the harness sits right below the tabs. Before you run a tab it is the harness as it is now; run it, or press a button, and watch the picture change. Switching back to a tab shows what its step drew, from the cache.

    Pages: Spaces that wrap spaces

  • 2026-10-25: In “Spaces that wrap spaces”, the picture now appears right under the button or cell you just pressed, drawn by the harness after that step, with what changed. A row of stage buttons above it shows any stage’s picture in the same place, so you can compare two states without scrolling to the tabs.

    Pages: Spaces that wrap spaces

  • 2026-10-25: “Spaces that wrap spaces” now drives Failover and the bulkhead too, in six more stages. Call a service whose primary is slow and watch the failover move to its backup, mend it and watch the route move back; then start five calls at once and watch a two-slot bulkhead run two and park three, and drain them as you move the harness clock, each call answering at its true time. Every stage’s picture now marks what changed since the stage before, and the line above the tabs keeps the failover’s route and the bulkhead in view as well as the circuit.

    Pages: Spaces that wrap spaces

  • 2026-10-24: “Spaces that wrap spaces” now walks through the resilience harness in numbered stages. Each tab holds a step to run beside what it did: the breaker’s state as the harness drew it at that stage, in the colors of the book’s theme, what changed since the stage before (marked, and said in words), and the harness time. Your own button presses add stages of your own after the chapter’s, so you can flip between any two states, and a line above the tabs keeps the upstream’s mode, the circuit and the harness time in view. The chapter also explains why showing a stage again costs nothing: its picture is cached until a governor’s state changes.

    Pages: Spaces that wrap spaces

  • 2026-10-24: “Spaces that wrap spaces” now runs in the page: a resilience harness puts the real throttle governors around a stub dependency you control. Set it failing and watch the circuit breaker trip, wait out the cooldown and watch the probe close it, watch a retry spend the run’s budget, and watch a timeout fire and trip the breaker too, with the arrangement and the breaker’s state drawn live beside buttons that drive it. Time is the harness’s own clock, so every run answers what the chapter shows.

    Pages: Spaces that wrap spaces

  • 2026-10-23: A new book. It teaches the ikigai architecture as one substrate read two ways: the patterns a person operates are the patterns that authorize and confine them. An introduction sets out resource-oriented computing and how it generalizes the Web, then six parts carry one worked resource, a ledger item, through the capability spine and the manifold-driven UI, the door, the overlay chain, the economics of golden threads and caching, and presence and the audit axis. The capability and economics chapters run in the page: you narrow your own authority and watch an action leave the manifold, and you cut a golden thread and watch a view recompute.

    Pages: Introduction, One mechanism, read two ways, Resolve the viewer’s authority once, The manifold, and the UI that falls out of it, Golden threads and the cost of a read

One mechanism, read two ways

Take one resource and hold it still: a ledger item. In The gonk Book you built a whole host around items like this one; here it is just a resource, and we are going to look at it from every side.

File one now. This page carries its own kernel — the same work ledger, compiled to WebAssembly — so the item you file is real, and every later cell on this page reads the ledger you are building.

#1 urn:iki:ledger:default:item:…
[uncacheable]

It has a number for people and an IRI for machines. Read it back:

   #1  open    p-  Translate the research note
  iri:      urn:iki:ledger:default:item:…
  filed:    …
  updated:  …
[computed]

Now look at what just happened, because the whole book is in it.

  • You operated the item. sink … append is a thing a person does, and source … item:1 is a page a person reads. This is the item’s user-experience face: a URL you can link to, a verb you can invoke, an answer you can render.
  • The kernel authorized you. The write was allowed because the capability you hold grants it; a reader who did not hold it would have been refused. This is the item’s security face: a name resolved under an authority, an action admitted or denied.

Those are not two systems. They are one resolution, read two ways. The name you linked to is the name the capability check keyed on. The verb you invoked is the unit the authority gates. The representation you rendered is the representation a converter produced. There is no separate “security object” behind the item that the page knows nothing about; the page and the check are looking at the same resource.

Why this is the architecture, and not a slogan

It would be easy to read “one substrate, two faces” as a tidy way to describe two things that are really separate. It is not. The claim is concrete, and the rest of the book is six demonstrations of it on this one item:

  • The capability that lets you write the item is a resource, attenuated the same way down every call; the screen that asks a person to consent to a delegation reads the same grant the kernel will later check. One grant, a security artifact and a UX surface.
  • The action list the kernel offers for the item under your capability is both the set of things you are authorized to do and the set of buttons a page would draw. An action you lack is not shown disabled; it is not shown. One list, an authorization and a menu.
  • The door that serves the item to a browser is the one place that authenticates you, and the one place that sets the headers and escapes the output. One module, a boundary and a surface.
  • The cache that makes a second read of the item cheap is keyed on who asked, so a cached answer computed under one authority is never handed to another. One index, a performance win and a confidentiality boundary.

Each of those has a chapter. What they have in common is the move you just watched: a person does a thing, and the doing of it is the authorizing of it, because both are the kernel resolving a name under a capability. Teaching the architecture means teaching that resolution once — which the next chapter does — and then noticing, each time, that the thing you built for the person is already the thing that confines them.

A word on what you will and will not see run

The demonstrations in the capability and economics chapters run in this page, against this kernel, for real: you will narrow your own authority and watch an action vanish, cut a golden thread and watch a view recompute. A handful of patterns in the overlay and closing chapters depend on parts of the system a browser cannot stand in for — a real network peer, a real passkey ceremony end to end, the hash-chained log on disk. Those are drawn as pictures and labeled as pictures, with the runnable version promised as the tooling that draws state (not just arrangement) lands. The book never shows you a recording and calls it a run.

Resolve the viewer’s authority once

Every request the kernel answers is answered under a capability — the authority of whoever is asking. This chapter is about where that authority comes from, how it narrows, and the one rule that makes the whole security model tractable: the viewer’s authority is resolved once, at the edge, and there is no ambient root underneath it. Nothing deep in the system quietly has more power than the request that reached it.

If you have read The ikigai Book you have met capabilities and clamp. Here we follow the spine end to end — capability, clamp, principal, grant, corridor — on the worked item, and watch an attenuation take effect for real.

Capabilities: a resource that only narrows

A capability is named like everything else (urn:cap:…) and carries a set of scopes. The one operation that matters is clamp (attenuation): intersect a capability with a set of scopes to get a narrower one. Clamp can only take authority away. There is no widen. That asymmetry is the whole safety story: a request handed a clamped capability cannot climb back out of it, however deep it goes, because every sub-request it makes is answered under the same narrowed authority.

File an item so there is something to act on, then narrow your own authority to a reader’s and try to write:

#1 urn:iki:ledger:default:item:…
[uncacheable]
narrowed — capability: urn:cap:ledger:read:default, urn:cap:store:read:graph:*
error: denied: capability does not grant `urn:cap:store:write:graph:*` (declared by `urn:iki:ledger:comment`; any grant under `urn:cap:store:write:graph:` satisfies it)

Two things to notice. The refusal is a Denied, not a broken write and not a silent no-op: the kernel’s capability check is the only authorization, so a write the authority does not cover never reaches the store. And the refusal is legible — it names the exact scope that would have allowed it. A person can read that, and so can an agent; a refusal is an authority fact, not an opaque failure. Put your authority back before moving on:

reset to identity — capability: root (full authority)

(In a real deployment you do not hold root; a door hands you exactly your grant and nothing wider. The page gives you root so you have something to narrow from.)

The principal: one identity, minted at the edge

Authority has to start somewhere, and in ikigai it starts at a door — the component that turns a proof (a passkey assertion, a client certificate, a token) into a principal, a capability scoped to one identity. One person is one principal whatever credential they used. The principal reaches every endpoint at every depth of a resolution, and — this matters for the economics chapter — it partitions the cache, so an answer computed under one principal’s authority is physically not reachable under another’s.

A session, crucially, caches authentication only, never authority: “this is still Dana” is cached; “Dana may close this item” is resolved fresh each request. Authority that was cached would be authority that could not be revoked, and a system that cannot revoke cannot be trusted.

The grant: what a delegation is made of

Between a principal and an action sits a grant: a small record with a grantee, a scope, and an expiry (notAfter). Its reader is fail-closed — a grant that cannot be read is a grant that does not apply. When a person delegates to an agent, the agent’s grant is a subset of the person’s, checked by the kernel, and the delegation is revocable. This is the resource the next chapter’s consent screen renders: the security object and the UX surface are the same record read twice.

Corridors: a clamp with a name

A space (The ikigai Book’s dynamic spaces are the mechanism) can wrap other resources, and a space that can only clamp is a corridor. A personal space is a corridor whose authority is the viewer’s own — “my items” is the ledger seen through a clamp to me. A tenant’s graph behind a corridor they lack the capability for is not denied to them; it is not there — the name does not resolve at all, so there is nothing to probe. Corridors stack by intersection, so wrapping a corridor in another corridor can only narrow further.

That is the spine: authority minted once at a door as a principal, carried as a capability, narrowed by clamp and by the corridors it passes through, spent on grants the kernel checks and nothing else trusts. Everything the person sees is built on top of it — which is the next chapter.

The manifold, and the UI that falls out of it

The last chapter resolved a viewer’s authority. This one asks the kernel a question that authority answers directly: what can I do here? The answer is the action manifold — the set of actions available to the caller under their capability — and the quiet claim of this chapter is that the manifold a safe agent reads and the action list a page draws are one list.

Watch the list narrow

File an item, then ask the kernel for the whole manifold. Under full authority it is long: every ledger action, every store query, the kernel’s own meta endpoints.

#1 urn:iki:ledger:default:item:…
[uncacheable]
urn:iki:ledger:lifecycle:{name}
urn:iki:ledger:policy:{name}
urn:iki:store:graphs
urn:iki:store:info
urn:kernel:actions
urn:kernel:aliases
urn:kernel:cache
urn:kernel:cached
urn:kernel:catalog
urn:kernel:constraint
urn:kernel:cut
urn:kernel:dependents
urn:kernel:explain
urn:kernel:scheduler
urn:kernel:threads
urn:kernel:topology
urn:kernel:uncached
urn:kernel:validate
urn:iki:ledger:ledgers
urn:iki:ledger:{ledger}:item:{id}
urn:iki:ledger:{ledger}:item:{id}:about
urn:iki:ledger:{ledger}:item:{id}:closed
urn:iki:ledger:{ledger}:item:{id}:holder:{holder}
urn:iki:ledger:{ledger}:item:{id}:state:{value}
urn:iki:ledger:{ledger}:label
urn:iki:store:graph-info
urn:iki:store:graph-update
urn:iki:store:update
urn:iki:ledger:{ledger}:defer
urn:iki:ledger:{ledger}:item:{id}:state
urn:iki:store:load
urn:iki:ledger:{ledger}:claim
urn:iki:ledger:{ledger}:doctor
urn:iki:ledger:{ledger}:link
urn:iki:ledger:{ledger}:purge
urn:iki:ledger:{ledger}:reopen
urn:iki:store:dump
urn:iki:ledger:{ledger}:close
urn:iki:store:ask
urn:iki:store:construct
urn:iki:store:describe
urn:iki:store:graph-ask
urn:iki:store:graph-construct
urn:iki:store:graph-describe
urn:iki:store:graph-select
urn:iki:store:select
urn:iki:ledger:{ledger}:comment
urn:iki:ledger:{ledger}:next
urn:iki:ledger:{ledger}:append
urn:iki:ledger:{ledger}:count
urn:iki:ledger:{ledger}:items
[computed]

Now narrow your authority to a reader’s — the ledger-read family and the store-read family — and ask again:

narrowed — capability: urn:cap:ledger:read:*, urn:cap:store:read:graph:*
urn:iki:ledger:lifecycle:{name}
urn:iki:ledger:policy:{name}
urn:iki:store:graphs
urn:kernel:actions
urn:kernel:validate
urn:iki:ledger:ledgers
urn:iki:ledger:{ledger}:item:{id}
urn:iki:ledger:{ledger}:item:{id}:closed
urn:iki:ledger:{ledger}:item:{id}:holder:{holder}
urn:iki:ledger:{ledger}:item:{id}:state:{value}
urn:iki:store:graph-info
urn:iki:ledger:{ledger}:item:{id}:state
urn:iki:ledger:{ledger}:doctor
urn:iki:store:graph-ask
urn:iki:store:graph-construct
urn:iki:store:graph-describe
urn:iki:store:graph-select
urn:iki:ledger:{ledger}:next
urn:iki:ledger:{ledger}:count
urn:iki:ledger:{ledger}:items
[computed]

append, comment, close, claim, link, graph-update are gone. Not greyed, not disabled — absent. The manifold narrowed because it is computed from your capability, and a capability that cannot write has nothing to say about a write action.

Why this is the user interface, not a report about it

This is the crossover worth stating plainly: an agent’s tool list is the manifold under its capability, and a page’s action list is the manifold under the viewer’s capability — one sentence.

A page built this way draws its buttons from the manifold and its forms from each action’s self-description (its argument names, their types, which are required, which take an enumerated value). The consequence is a UI that cannot lie about authority: an action the viewer lacks is not rendered at all, so there is no disabled button inviting a person to wonder why, and no form field offering input the action would refuse. The thing that makes an agent’s tool list safe — it only contains what the agent may do — is the thing that makes a human UI honest.

Put your authority back:

reset to identity — capability: root (full authority)

The manifold over-offers on purpose; the check is exact

There is a subtlety here that is itself a lesson. The manifold matches on a family: an action appears when you hold some grant under its declared prefix (urn:cap:ledger:read:* means “some ledger-read grant”). But a ledger grant names exactly one ledger, and a store-read grant names exactly one graph. So holding the family wildcard is enough to be offered next, and not enough to resolve it:

denied: reading <urn:iki:ledger:graph:default> through `urn:iki:store:graph-select` needs the grant
`urn:cap:store:read:graph:urn:iki:ledger:graph:default`, which this capability does not hold. Every
graph a scoped read names must be granted: the read is refused rather than answered over the graphs
that are, because a narrower dataset than the one asked for returns rows that look right and are not.

This is deliberate, and it is the right shape. The manifold is a menu, and a menu may list a dish the kitchen then declines to serve you; what it must never do is omit a dish you could have had. Over-offering on the family and enforcing on the exact grant keeps the menu complete while the kernel’s check stays the only authorization. The declaration a UI reads is the wildcard; the authority the kernel spends is exact.

When a person delegates — to a teammate, to an agent — the screen that asks them to approve it is not a separate security dialog. It reads the same grant the last chapter described and the same self-description the manifold’s forms come from, and it shows the attenuation as a diff in human words: “this delegate gets read on your project’s ledger — a subset of your read and write.” The security object (grantee, scope, expiry) and the UX surface (a sentence a person can approve) are one record read twice. The devices-and-sessions page is the same grant from the other side: a list with “last used” and a revoke button per row.

Denied is not NotFound, and the difference is a UX decision

You saw a refusal above that named the missing scope. That is the non-sensitive case: the kernel separates “you may not” (Denied) from “there is nothing here” (NotFound), and where existence is not a secret it serves the Denied plus an explanation — the withheld-notice, which tells a person (and an agent) exactly which authority would admit the action.

But an explanation is itself a signal that the thing exists. For a sensitive resource — one whose very presence should not leak to someone who may not see it — the kernel collapses Denied into NotFound: the “you may not” and the “there is nothing here” become the same response, so a probe cannot tell them apart. The one hard rule underneath both cases is security-motivated and non-negotiable: a refusal is never dressed up as a successful page. Whether a resource explains its refusal or hides its own existence is a per-resource sensitivity decision — the same mechanism, the error experience and the confidentiality control, turned to the light twice.

One module, a boundary and a surface

Everything so far has been inside the kernel: a capability, a manifold, a grant. A door is where that inside meets a browser. It is the component that accepts a request from the outside, turns a proof of identity into a principal, hands the resolution exactly that principal’s authority and no more, and serves the answer back as a page. The claim of this chapter is that a door is the single clearest example of the whole book’s thesis, because it is at once a security boundary and a user-experience surface, and it is written once so that every page gets both.

What a door is responsible for

        request  ──▶  ┌───────────────────────────────────────────┐
                      │                  the door                 │
                      │                                           │
   proof (passkey,    │  1. authenticate → mint a principal       │
   certificate,       │  2. admit by Origin; mutations need       │
   token)             │     Origin + SameSite (never a bare GET)  │
                      │  3. resolve under exactly that authority  │
                      │  4. set security headers (CSP, …)         │
                      │  5. escape every value into the page      │
                      │                                           │
                      └───────────────────┬───────────────────────┘
                                          │
                       page  ◀────────────┘   (or a legible refusal,
                                               never a 200 success shell)

Read that list once as a security engineer and once as a front-end engineer. The security engineer sees authentication, CSRF defense, a content-security policy, and output encoding. The front-end engineer sees sign-in, forms that submit safely, a page that renders, and errors that read well. They are the same five steps. A door that did the security half without the UX half would serve blank pages; one that did the UX half without the security half is the vulnerability. Writing the door once is how a project stops re-deciding those five things, differently and wrongly, per page.

The escaping module, written once

Step five — putting a value into a page — is where injection lives, and it is also where a broken link lives, and those turn out to be the same problem solved by the same small module.

Consider two functions such a module provides: one that escapes text for HTML, and one that makes a URL safe to use as a link. The first prevents a value containing markup from becoming markup — a security property. The second makes a value that is not a valid, safe URL render as plain text instead of as a broken or dangerous link (a javascript: URL, say) — a correctness and UX property. One module, one set of rules, and — because the same rules are needed in Rust, in an XSLT transform, in a page’s own script — one shared set of test vectors that every language’s copy must pass. A bug fixed in the escaper is fixed everywhere; a divergence between two hand-rolled escapers is exactly the class of bug this consolidation removes.

The discipline behind it is worth stating because it generalizes: do not re-implement behavior a standard already defines. Escaping, percent-encoding, auth headers, digests — each wants one implementation in its natural module, called everywhere, not a private copy per caller that drifts.

The content type carries a trust decision

Picking which representation to send a client is content negotiation — a reach decision, the UX face of transreption. The same Content-Type carries a security decision. HTML that the door authored is served with the strict content-security policy, because the door knows what is in it. HTML it did not author — a value a caller supplied, a document from a writable mount, a representation from a peer — is served sandboxed, or as text/plain, never same-origin with scripts it did not vet. The media type is simultaneously “what shall I show you” and “how much do I trust this to run,” and the door decides both at once.

What a URL may and may not carry

A door makes the system’s addressing real: a URL path is a resource IRI, so links are stable and the browser’s Back button works. That convenience comes with a companion rule that is pure security: a URL encodes a resource’s declared arguments and nothing else — no hidden UI state the resource did not declare, and above all no bearer token and no personal data in a query string. URLs are logged, shared, and cached; the addressing convention and the leak-prevention rule are the same rule seen from two sides.

Bodies larger than memory

A door also frames what it sends and bounds what it takes, and since the streams release it does both without holding a whole body. What follows is ikigai serve --http from ikigai-cli 0.2.2 (ikigai-web 0.2.2, built with the cli’s web feature), with the file answers of 0.2.3; The ikigai Book’s part on streams says what a body is.

  • Framing. An answer that is one buffer goes whole, with Content-Length, as every answer did before. An answer that streams is framed by Content-Length when its length is known and chunked when it is not. A stream that fails part-way fails visibly: a chunked answer ends without its last chunk, a measured one ends short, and the connection closes; never a well-formed shorter body.
  • Ranges. A GET over a body that can seek, knows its length and has a strong validator says Accept-Ranges: bytes and honors a Range of one byte range: 206 Partial Content, or 416 Range Not Satisfiable when the range lies past the end. If-Range is compared strongly against the ETag, and anything else (a mismatch, a date, several ranges, a Range that does not parse) is answered with the whole body and 200. A file is such a body from 0.2.3, so a file download resumes.
  • Uploads. A route named an upload route (http.upload in the config home) does not read its body into memory: the door writes it to a spill under <config home>/spill/http-<port> as it arrives, then hands the sink the content’s id. Each bound is a refusal, never a truncation: 413 past the route’s largest body (checked against Content-Length before a byte is read) or past the caller’s spill grant, 408 for a body that falls behind the minimum rate after its grace (http.upload_min_rate, http.upload_grace), and 503 when the uploads in flight hold the most the disk may (http.spill_total; http.spill_ceiling bounds one request). A caller granted no spill is served as before, its body in memory under the door’s ordinary bound.
  • Validators. Every ETag is now one rule over the type and the content id, whatever the body’s shape, so a buffer and a stream of the same bytes carry the same tag. Before 0.2.2 the hash ran over the bytes themselves, so every tag changed once: a client’s cache revalidates each stored answer once, a 200 where it had a 304, and is right from then on.

The bounds are the security half and the framing is the UX half, and they are, once more, the same decisions: a download that cannot pretend to have finished and an upload that cannot fill the disk are one rule seen from two sides.

Where this runs, and where it is drawn

A browser’s content-security policy and its Origin checks are enforced by the real browser, which a kernel compiled into a page cannot stand in for. So the door is the one pattern in this book that is partly a picture: the kernel logic runs in the page, but the headers above are shown as annotations rather than exercised, and the escaping is the piece you can run for real against reader input. The boundary is real in a deployment; here it is drawn, and labeled as drawn.

Spaces that wrap spaces

The capability spine decided who may reach a resource. This chapter is about what happens to a request on the way — the governors that make a resource resilient and bounded: rate limits, retries, circuit breakers, failover, timeouts, and concurrency caps. In ikigai these are not features baked into each endpoint. They are overlays: a space wraps another space and changes how requests pass through it, and because a space can wrap a space, they stack.

This builds directly on dynamic spaces from The ikigai Book. An overlay is a space whose job is not to answer but to govern the space it wraps, so a wrapped resource answers exactly as before until a governor has something to say — at which point what it says is a refusal.

   request
     │
     ▼
  ┌──────────────────────────────────────────────────┐
  │ Retry (idempotent verbs, spends a budget)        │
  │  ┌─────────────────────────────────────────────┐ │
  │  │ CircuitBreaker (open / closed / half-open)  │ │
  │  │  ┌────────────────────────────────────────┐ │ │
  │  │  │ Timeout (a deadline on the call)       │ │ │
  │  │  │  ┌───────────────────────────────────┐ │ │ │
  │  │  │  │  the space being guarded          │ │ │ │
  │  │  │  │  (a door, a mount, an endpoint)   │ │ │ │
  │  │  │  └───────────────────────────────────┘ │ │ │
  │  │  └────────────────────────────────────────┘ │ │
  │  └─────────────────────────────────────────────┘ │
  └──────────────────────────────────────────────────┘

The three facts every overlay reads

The overlays share a vocabulary. Each one makes its decision from the same three facts about a request, and no more:

  • Is the verb idempotent? A Source or an Exists can be safely re-issued; a Sink that is not idempotent cannot. Retry and Failover refuse to re-run what is unsafe to re-run.
  • What class is the failure? A failure is transient (worth another try or a different target) or permanent (do not bother). A 429-style “try again after” carries its own delay.
  • What is left of the retry budget? A single counter shared across the whole resolution tree.

That third fact is the one security hook in the resilience family, and it is worth dwelling on. Nested retries multiply: a Retry wrapping a space that itself wraps a Retry could turn one request into many. A shared retry budget caps the total number of re-issues across the entire tree, so no arrangement of nested governors can amplify one request into a storm. It is a blast-radius bound, expressed as authority — you hold a budget, and when it is spent, re-issues stop.

The two faces, overlay by overlay

Here is the split that makes this chapter part of the book rather than an appendix. Every governor has a security reading and a UX reading, and the verdict is which one it is primarily for:

overlaysecurity faceUX faceprimarily
RateLimitabuse / denial-of-service control; politeness to a published ratea “try again after” lets a client back off instead of hammeringboth
Retryonly the shared budget, capping amplificationhides a transient blip; fewer visible failuresUX
CircuitBreakerblast-radius containment: stop feeding a dead dependency; recover deliberatelyfail fast instead of hanging the person on a dead backendboth
Failover— (availability, not a security control)continuity: move to a backupUX
Timeouta resource-exhaustion bound: a worker is never held forevernever hang; drive the loading / “did not answer” stateboth
Throttle / Bulkheadisolation: one tenant or hot prefix cannot starve the poolbackpressure — park the excess, never an errorboth

The pattern in the table is the pattern of the whole book. A refusal that protects the dependency or the host (RateLimit, CircuitBreaker, Timeout, Throttle) has a security face; a re-issue that protects the caller’s experience (Retry, Failover) is UX-facing, with the budget its only security hook. Same mechanism, read by what the refusal is for.

Transparency: a governor you can see through

One property makes all of this drawable, and it is worth naming because it is unusual. The overlays are transparent to structure: a governor reports the topology, the identity, and the cache identity of the space it wraps. So Retry(CircuitBreaker(Timeout(space))) still reports the doors underneath it; the system’s own introspection sees through the stack to what is guarded. What a governor adds is only ever a refusal — and the kernel never caches a refusal — so the arrangement can be harvested from the topology while the governor’s state (a breaker open or closed, a budget’s remaining count) is a separate thing drawn on top.

The governor as a constraint made explicit

Step back and the whole chain has one organizing idea, borrowed from the theory of constraints (Goldratt): a system’s throughput is set by its bottleneck, and the useful moves are to exploit the bottleneck, subordinate everything else to it, and make the constraint explicit and enforceable. The overlays are the governance half of that: a rate, a timeout, a concurrency cap is a constraint written down as a resource and enforced by the kernel, so “do not exceed this” stops being a hope in a code review and becomes a refusal at resolution. Non-conforming behavior simply fails to resolve. (The throughput half of the same idea — cache and golden thread as ways to exploit the constraint — is the next chapter.)

Drive the overlays yourself

The box diagram above is a picture. Below it runs: a resilience harness in this page, the real ikigai-throttle governors wrapped around a stub dependency you control, so you can make the dependency fail, stall or push back and watch what each governor does about it. Nothing is mocked except the dependency. The breaker that trips here is the CircuitBreaker every ikigai host runs, and the timeout is its Timeout.

The harness is a kernel of its own (crates/resilience-harness), started the first time you press Run in a cell below that says “in the resilience harness”, or a button beside the picture. It binds a handful of names:

  • urn:iki:tutorial:harness:mode, the one thing you write: what the stub does when called. ok answers; slow takes 10 seconds, past the timeout’s budget; fail fails transiently (unavailable); limited asks to be called again after a second (retry after). Anything else is refused as an invalid argument.
  • urn:iki:tutorial:harness:upstream, the stub itself, ungoverned. It numbers every call it receives, so an answer tells you whether a call reached it.
  • urn:iki:tutorial:harness:call, the same stub behind the stack this chapter draws: Retry(CircuitBreaker(Timeout(upstream))).
  • urn:iki:tutorial:harness:clock, the harness’s time. It moves only when you move it (Sink a duration such as 5s) or when a governor waits.
  • urn:iki:tutorial:harness:service, the same stub again, in a primary space, behind a Failover whose second space is a backup that always answers.
  • urn:iki:tutorial:harness:work, the stub once more, behind a Throttle that lets two calls run at once, and urn:iki:tutorial:harness:burst, which starts several calls to it at once (Sink a number such as 5).
  • urn:throttle:state, the governors’ state in words, and urn:diagram:state, the harness’s arrangement with that state drawn over it: the picture below.

The stack is built like this, innermost first:

/// The harness's root: the governed call, the stub's own space, the board, and the
/// diagram, in that order.
///
/// The stack is the one the chapter draws, innermost first: `Timeout` bounds each invocation
/// of the upstream, `CircuitBreaker` counts the transient failures that come out of it
/// (a timeout among them), and `Retry` re-issues what is worth re-issuing from the run's
/// budget. Only the breaker has state worth showing (`Retry` and `Timeout` hold nothing past
/// one call), so it is the one observed on the board.
pub fn root(
    state: &Arc<HarnessState>,
    clock: &Arc<HarnessClock>,
    board: &StateBoard,
    background: &Arc<Background>,
) -> Arc<dyn Space> {
    let timer = Arc::clone(&background.timer);
    let stub: Arc<dyn Endpoint> = Arc::new(upstream(Arc::clone(state), Arc::clone(&timer)));

    let governed = EndpointSpace::new()
        .bind_arc(Exact::new(CALL), Arc::clone(&stub))
        .named(space_iri("tutorial:harness:governed"));
    let timed = Timeout::new(governed, TIMEOUT).with_timer(timer.clone());
    let breaker = CircuitBreaker::new(timed, THRESHOLD, COOLDOWN)
        .with_clock(Arc::clone(clock) as _)
        .observed(board, GOVERNOR)
        .expect("one governor, named once");
    let retry = Retry::new(breaker, ATTEMPTS)
        .with_timer(timer.clone())
        .with_max_wait(MAX_WAIT);

    // Failover: the stub in a primary space, bounded by the same timeout, else the backup.
    let primary = EndpointSpace::new()
        .bind_arc(Exact::new(SERVICE), Arc::clone(&stub))
        .named(space_iri("tutorial:harness:primary"));
    let spare = EndpointSpace::new()
        .bind(Exact::new(SERVICE), backup())
        .named(space_iri("tutorial:harness:backup"));
    let failover = Failover::new(vec![
        Arc::new(Timeout::new(primary, TIMEOUT).with_timer(timer.clone())) as Arc<dyn Space>,
        Arc::new(spare),
    ])
    .observed(board, FAILOVER)
    .expect("one governor, named once");

    // The bulkhead: the stub again, at most SLOTS calls at once; the rest park.
    let pool = EndpointSpace::new()
        .bind_arc(Exact::new(WORK), Arc::clone(&stub))
        .named(space_iri("tutorial:harness:pool"));
    let bulkhead = Throttle::new(pool)
        .limit(WORK, SLOTS)
        .observed(board, BULKHEAD)
        .expect("one governor, named once");

    let own = EndpointSpace::new()
        .bind(Exact::new(MODE), mode(Arc::clone(state)))
        .bind(
            Exact::new(CLOCK),
            self::clock(Arc::clone(clock), Arc::clone(background)),
        )
        .bind_arc(Exact::new(UPSTREAM), stub)
        .bind(Exact::new(BURST), burst(Arc::clone(background)))
        .named(space_iri("tutorial:harness:upstream"));

    Arc::new(Fallback::new(vec![
        Arc::new(retry) as Arc<dyn Space>,
        Arc::new(failover),
        Arc::new(bulkhead),
        Arc::new(own),
        Arc::new(board.space().named(space_iri("tutorial:harness:board"))),
        Arc::new(ikigai_diagram::space()),
    ]))
}

with these settings: a call may run 2 seconds; the breaker trips after 3 consecutive transient failures and stays open for 5 seconds; Retry waits out a retry after of at most 2 seconds; and the kernel gives every run a retry budget of 2 re-issues in all, whatever Retry’s own attempts would allow:

/// How long `Timeout` lets a call run.
pub const TIMEOUT: Duration = Duration::from_secs(2);
/// How long the upstream takes in `slow` mode: past the timeout's budget.
pub const SLOW: Duration = Duration::from_secs(10);
/// Consecutive transient failures that trip the breaker open.
pub const THRESHOLD: u32 = 3;
/// How long an open circuit refuses before it lets one call through as a probe.
pub const COOLDOWN: Duration = Duration::from_secs(5);
/// The most invocations `Retry` makes of one call: more than the budget allows, so the
/// budget is what stops it.
pub const ATTEMPTS: u32 = 5;
/// The longest `RetryAfter` delay `Retry` waits out; a longer one is returned unchanged.
pub const MAX_WAIT: Duration = Duration::from_secs(2);
/// The delay the upstream asks for in `limited` mode.
pub const LIMITED_DELAY: Duration = Duration::from_secs(1);
/// Re-issues one run may spend in all, across every overlay: the run's retry budget.
pub const RETRY_BUDGET: u32 = 2;
/// How many calls to [`WORK`] the bulkhead lets run at once; the rest park.
pub const SLOTS: usize = 2;
/// The most calls one burst may start.
pub const MAX_BURST: usize = 10;

Why the harness has its own clock. A breaker’s cooldown, a timeout’s budget and a retry’s wait are all time, and on the wall clock this page would answer differently for every reader and for every run of the test that replays it. So the harness clock moves only when you say (“wait 5 seconds”) or when a governor waits, and a governor’s wait is virtual: the earliest wait pending finishes first and moves the clock to its deadline, in no real time. A timeout racing a 10-second call against its 2-second budget fires at 2 seconds, exactly, on every page, and the call it abandoned never finishes. That makes every answer below the same for you as for the chapter.

What the picture draws, and what it cannot. The circuit breaker and the bulkhead appear in the picture as governors, each in the box of the space it governs. That is the transparency property above at work: Retry and Timeout hold nothing past a single call (an attempt count, a deadline), so they have no state to draw, and they report the topology of the space they wrap, so the picture sees through them to the door they govern. Failover is the exception: it encloses two spaces and picks the one that answers only when it is called, and no kind of space says that, so it reports no structure at all, and the picture draws it as an opaque box. Its state, which space the last call ended at, is said in words below the picture with everyone else’s.

The walkthrough, in stages

The walkthrough below is a sequence of stages. A stage is the harness after a step that changed its picture, and each one is a numbered tab. Below the tabs, two pictures sit side by side (stacked on a phone): After is the open tab’s stage, and Before is the stage you compare it with, the one just before unless you pick another from the “Compare with” list above them. Under them is what changed from Before to After, in words (the board’s words, compared line by line: the words that differ are marked, and the line they replaced is said beside them). Below that is the tab’s step: a sentence or two and the cells you run.

Each picture says whose it is. Until you run a tab’s step, its picture is the chapter’s own, in a dashed frame; run it, and After becomes the picture your page drew, in a solid frame. Run the tabs in order: each step starts where the one before left the harness. A step that changes nothing the picture shows makes no stage, and the picture says “no change” instead. Twelve stages: the breaker from every circuit closed to a circuit half-open again, then the failover moving to its backup and back, then the bulkhead filling and draining. (The tabs are the books’ stages viewer, js/stages.js, which knows nothing about governors: the page names the line that draws the picture and the line that says it in words, and the viewer does the rest.)

Watch the governors, not the upstream. The picture draws the governors, not the stub they govern. Setting the stub’s mode changes nothing in the picture: a governor learns of a mode only when a call goes through it. The line above the tabs keeps the facts that change current, each read from the name that holds it after every step: the upstream’s mode (urn:iki:tutorial:harness:mode), the circuit (urn:throttle:state:breaker), the failover’s route (urn:throttle:state:failover), the bulkhead (urn:throttle:state:bulkhead) and the harness time (urn:iki:tutorial:harness:clock).

The picture marks what changed. After every step the page also reads the board as Turtle (urn:throttle:state as=text/turtle) and hands it to the next read of the picture as before= (ikigai-diagram 0.2.4): each key that changed since is marked in the picture, after its state word (OPEN (was CLOSED), (new)), and the words in the picture begin with what changed. The chapter’s pictures are marked against the stage before; the first stage you make yourself is not marked, because the page reads nothing until you take a step.

The buttons are yours. They drive the same harness the cells do. A press that changes the picture adds a stage after the chapter’s, numbered on, and opens it as After, beside the stage the harness was at before the press, so you can compare any two states the harness has been in.

Every picture is drawn once. A stage’s picture is urn:diagram:state, a pure composition of two resources: the harness’s topology and the governors’ board. It is cacheable, and it hangs from what it read: the board hangs from the breaker’s golden thread, urn:throttle:governor:breaker, which the breaker cuts on every change its board shows (a failure counted, a circuit tripped or closed). A cooldown running out cuts nothing; the board’s answer is cached only until it would run out, on the harness clock. So a picture stays true until the state moves. After every step the page asks for it as your own lines would: a step that moved a governor cut its thread, so the picture is drawn again, once; a step that changes nothing cuts nothing, so the page’s read is a cache hit, and the page says so (stage 4 shows one in a trace). Switching tabs, or what you compare with, asks for nothing at all: each tab keeps the picture its step drew. The chapter’s twelve pictures are files the same harness drew natively, held to it byte for byte by a test, so they show before the page’s kernel loads, without it, and in print. Every picture is kept in both the light and the dark colors (scheme=light, scheme=dark), and the page shows the one the book’s theme needs.

  • upstream: ok
  • no circuit has failed yet
  • no call has gone through the failover yet
  • bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
  • the harness clock reads 0s

Stage 1: the start

The harness as it starts. No call has been made, so no circuit has failed, and the breaker holds nothing but its settings.

The cells in the stages assume you run them in order on a fresh page. If you have pressed the buttons first, the cells answer from where you left the harness and say they differ from the chapter, and a cell that then changes something adds a stage of yours. Reloading the page starts the harness over.

upstream: ok · no circuit has failed yet · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 0s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup

Stage 2: the breaker trips

Set the stub failing. This changes nothing in the picture: the mode is the stub’s.

the upstream is now fail
[uncacheable]

Make one governed call. It fails, but look at the call number:

error: unavailable: upstream failed (call 3)

One call from you was three calls to the stub. unavailable is transient, so Retry re-issued it, twice, until the run’s budget of 2 re-issues was spent; and the breaker below it counted every one of the three failures. Three is its threshold, so the circuit is now open:

upstream: fail · circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 0s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup

The board says so, in words (served from the cache: the line above the tabs read the breaker’s key after the call, and nothing has cut it since):

breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures
[cached]

Call again:

error: retry after 5000 ms: circuit open for `urn:iki:tutorial:harness:call`

No call number: this one never reached the stub. An open circuit fails fast, and it says when to come back (the cooldown left) as a retry after, the same class a 429 is. Retry saw that class too, and returned it unchanged, because 5 seconds is longer than it is allowed to wait. No change: a refusal is not a failure the breaker counts, so nothing it shows moved, and this step makes no stage.

Stage 3: the probe

Wait out the cooldown. The circuit is now half-open: the next call is let through as a probe.

the harness clock moved 5s: it reads 5s
[uncacheable]
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
[computed]

Nothing wrote the board between the last two reads of it, and the second was still computed afresh: the board’s answer was cached only until the cooldown would run out, measured on the same clock, so moving the clock past it was enough. Time alone made this stage:

upstream: fail · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 5s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup

Stage 4: the probe closes the circuit

Mend the stub and make the probe call:

the upstream is now ok
[uncacheable]
ok (call 4)
[uncacheable]
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures
[computed]

Call 4: the probe reached the stub, it answered, and the circuit closed. Had the stub still been failing, the probe’s failure would have opened the circuit again for another cooldown.

upstream: ok · circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 5s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
No change: the retry spends its budget

Now make the stub push back: limited answers retry after 1000 ms. This time trace the call, so you can see inside it:

the upstream is now limited
[uncacheable]
trace  urn:iki:tutorial:harness:call
  client      ikigai repl  ·  capability: root (full authority)
  transport   embedded · in-process
 
urn:iki:tutorial:harness:call   upstream · failed · … · 2000ms · failed=retry-after answered-by=urn:iki:space:tutorial:harness:governed
├─ urn:iki:tutorial:harness:mode   harness-mode · computed · … · 0ms · answered-by=urn:iki:space:tutorial:harness:upstream
├─ urn:iki:tutorial:harness:mode   harness-mode · cached · … · 0ms · answered-by=urn:iki:space:tutorial:harness:upstream
└─ urn:iki:tutorial:harness:mode   harness-mode · cached · … · 0ms · answered-by=urn:iki:space:tutorial:harness:upstream
urn:iki:tutorial:harness:call   error: retry after 1000 ms: upstream limited (call 7)

Read it from the inside out. The stub reads its mode once per call, and there are three reads under the one call you made: three attempts, calls 5, 6 and 7. (The first read is computed because setting the mode a line earlier cut the mode’s thread; the other two are served from the cache.) A one-second delay fits inside Retry’s two-second bound, so it waited each one out before re-issuing, and the call took 2000 ms of harness time: two waits, two re-issues, and then the budget of 2 was spent and the third retry after came back to you. Retry would have made 5 attempts; the run’s budget is what stopped it. And the breaker, which a retry after says nothing to, has not moved:

the harness clock reads 7s
[uncacheable]
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: closed, 0 consecutive transient failures
[cached]
trace  urn:throttle:state
  client      ikigai repl  ·  capability: root (full authority)
  transport   embedded · in-process
 
urn:throttle:state   throttle-state · cached · … · 0ms · answered-by=urn:iki:space:tutorial:harness:board   → …

The clock moved the two seconds the retry waited. The breaker’s key is the same answer as before, served from the cache: nothing a governor shows changed, so nothing cut its thread. No change, then, and the trace of the whole board says the same thing from the other side: urn:throttle:state was served from the cache, because the page read it when the stage happened, and nothing since has cut a thread it hangs from. The picture is a pure function of the board and the topology, so the page’s next read of it is a cache hit too.

Stage 5: the timeout

Make the stub slow: it now takes 10 seconds, and the call’s budget is 2.

the upstream is now slow
[uncacheable]
error: timeout: `urn:iki:tutorial:harness:call` exceeded 2000ms

The timeout names the target it bounded, and its class is transient, because the overlay cannot tell a slow dependency (which a repeat may beat) from expensive work (which it will not). So the two governors above it treated it like any other transient failure, and the circuit opened:

upstream: slow · circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 13s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup

The picture is the same as stage 2’s, because the state is: the breaker cannot tell a stalled dependency from a failing one, and does not need to. The clock and the board say how it got there:

the harness clock reads 13s
[uncacheable]
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: open, 3 consecutive transient failures
[cached]

Six seconds: Retry re-issued twice from the budget, each attempt was cut off at its 2 seconds, and the breaker counted three transient failures and opened. A stalled dependency trips the breaker exactly as a failing one does, which is the point of putting the timeout inside it.

Stage 6: the same stub, ungoverned

For contrast, call the same stub with no governor in the way:

ok, slowly (call 11, after 10s)
[uncacheable]

It answers, after the full 10 seconds. Without the stack, a caller waits as long as the dependency takes; with it, the caller hears in 2. And those 10 seconds were harness time: the clock moved past the open circuit’s 5-second cooldown while this call waited, so the circuit went half-open with nobody calling through it, the same way it did in stage 3:

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · no call has gone through the failover yet · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 23s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup

Stage 7: the failover moves to the backup

The stub is still slow. Call it through the failover instead of the breaker:

ok from the backup (backup call 1)
[uncacheable]

The primary is the same stub, behind its own 2-second timeout. It was cut off, and a timeout is transient, so Failover moved to the next space, the backup, which answered. The move is a re-issue, so it spent one retry from the run’s budget, as Retry’s do. The board now holds a route for the service: where its last call ended, at candidate 1 (the board counts a failover’s spaces from 0, the primary first). The failover itself is the opaque box in the picture, so the route is said in the words below it, and marked there as new.

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 1 · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 25s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 1.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 1.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 1

Stage 8: and back to the primary

Mend the stub and call the service again:

the upstream is now ok
[uncacheable]
ok (call 13)
[uncacheable]

A failover always tries its spaces in order, so the first call after the primary mends goes to the primary, and the route moves back. Nothing remembers that the primary was down: that is a breaker’s job, and wrapping the primary in a CircuitBreaker would make its trip-open the fast trigger to move on.

upstream: ok · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 0 · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 25s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 0

Stage 9: the bulkhead fills

A bulkhead parks only concurrent work, and a cell runs one line at a time, so the harness has a way to start several calls at once: Sink a number to urn:iki:tutorial:harness:burst, and that many calls to urn:iki:tutorial:harness:work start together, in the background, on the harness clock. Make the stub slow, so each call holds its slot for 10 seconds, and start five:

the upstream is now slow
[uncacheable]
started 5 calls to urn:iki:tutorial:harness:work at once: none answered and 5 calls still in flight
[uncacheable]

Two calls hold the bulkhead’s two slots, and the other three are parked: waiting, not refused. That is the difference between this governor and a rate limit: the excess is held until there is room, never turned away. The calls do not finish on their own; they move only when the harness clock does.

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 0 · bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 3 parked · the harness clock reads 25s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 3 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 3 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 3 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 0

Stage 10: two answer, two more start

Move the clock 10 seconds:

the harness clock moved 10s: it reads 35s; in the background, 2 calls answered and 3 calls still in flight
[uncacheable]

The two running calls answered at 10 seconds, which freed both slots, and two parked calls took them at that same instant. The harness runs its background work event by event: the clock steps to each wait’s deadline in turn, so a call that frees a slot lets the next one start at that moment, not at the end of the move.

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 0 · bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 1 parked · the harness clock reads 35s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 1 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 1 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 2 of 2 running, 1 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 0

Stage 11: the last one runs alone

Move the clock again:

the harness clock moved 10s: it reads 45s; in the background, 2 calls answered and 1 call still in flight
[uncacheable]

Nothing is parked now, and one slot is free: the bulkhead is admitting again.

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 0 · bulkhead `urn:iki:tutorial:harness:work`: 1 of 2 running, 0 parked · the harness clock reads 45s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 1 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 1 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 1 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 0

Stage 12: drained

And once more:

the harness clock moved 10s: it reads 55s; in the background, 1 call answered and none is in flight
[uncacheable]

Five calls, at most two at a time, each 10 seconds long: they answered 10, 10, 20, 20 and 30 seconds after they started, 30 seconds of harness time in all, where five calls at once would have taken 10. The bulkhead traded the last caller’s wait for a cap on how much of the stub any burst can hold.

upstream: slow · circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures · route `urn:iki:tutorial:harness:service`: last call answered at candidate 0 · bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked · the harness clock reads 55s

The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.The resilience harness, drawn: the governed call urn:iki:tutorial:harness:call inside its space; the failover, an opaque box, since a failover reports no structure; the bulkhead's space with urn:iki:tutorial:harness:work; the stub's own space with urn:iki:tutorial:harness:mode, urn:iki:tutorial:harness:clock, urn:iki:tutorial:harness:upstream and urn:iki:tutorial:harness:burst; then the governor state board and the diagram's space. The circuit breaker's state is drawn in the governed space's box and the bulkhead's in the bulkhead's space, every governor's state is said in words below the picture, and what changed since the stage before is marked. At this stage: throttle state: 3 governors visible; breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s; circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures; bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool; bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked; failover: failover over 2 spaces; candidate 0: governs urn:iki:space:tutorial:harness:primary; candidate 1: governs urn:iki:space:tutorial:harness:backup; route `urn:iki:tutorial:harness:service`: last call answered at candidate 0.

throttle state: 3 governors visible
breaker: circuit-breaker, governs urn:iki:space:tutorial:harness:governed, trips after 3, cooldown 5s
  circuit `urn:iki:tutorial:harness:call`: half-open, 3 consecutive transient failures
bulkhead: throttle, governs urn:iki:space:tutorial:harness:pool
  bulkhead `urn:iki:tutorial:harness:work`: 0 of 2 running, 0 parked
failover: failover over 2 spaces
  candidate 0: governs urn:iki:space:tutorial:harness:primary
  candidate 1: governs urn:iki:space:tutorial:harness:backup
  route `urn:iki:tutorial:harness:service`: last call answered at candidate 0

Rate limits are still only described here, by the table above: a RateLimit refuses where a Throttle parks, and the harness does not drive one yet.

Golden threads and the cost of a read

The Web caches by URL: a resource carries a time-to-live or a validator, and a client re-fetches when the clock says so or the validator changes. That is a guess about staleness, made per resource, in advance. ikigai does something exact instead. The kernel tracks what each answer was made from — its golden threads — and when something an answer depended on changes, it cuts precisely the threads that lead to it, and nothing else. This chapter is the system’s accounting: caching, golden threads, and why “add a source to this resource” is a line in the ledger, not a free gesture.

Watch a thread cut and a view recompute

File two items, with priorities, so the ledger’s ranked view has something to rank:

#1 urn:iki:ledger:default:item:…
[uncacheable]
#2 urn:iki:ledger:default:item:…
[uncacheable]

Ask for next, the ranked ready-set. The first read is computed — the kernel did the work:

 1.    #1  open    p0  Translate the research note
    p0 — priority 0; last updated …
 2.    #2  open    p1  Wire the live cells
    p1 — priority 1; last updated …
policy: priority-recency (weighs priority, recency, number)
ready: 2   excluded: 0
[computed]

Ask again, unchanged. The second read is cached — nearly free, because next is a pure function of the ledger graph and nothing the graph depends on has moved:

 1.    #1  open    p0  Translate the research note
    p0 — priority 0; last updated …
 2.    #2  open    p1  Wire the live cells
    p1 — priority 1; last updated …
policy: priority-recency (weighs priority, recency, number)
ready: 2   excluded: 0
[cached]

Now close item #2. A write is uncacheable by nature, and — this is the whole point — it cuts the golden threads that led to next:

closed #2 (done)
[uncacheable]

Ask for next one more time. It is computed again, and #2 is gone from the ready-set — and nobody told next to refresh:

 1.    #1  open    p0  Translate the research note
    p0 — priority 0; last updated …
policy: priority-recency (weighs priority, recency, number)
ready: 1   excluded: 0
[computed]

That is the mechanism in six cells. next never subscribed to anything, never polled, and holds no refresh logic. It declared, by being computed from the graph, that it depends on the graph; closing an item wrote the graph; the kernel cut exactly the threads that reached next and recomputed it on the next read. A view that depended on something the close did not touch would still be served from cache. This is cache invalidation that is neither a guess nor a manual chore — it is a consequence of naming dependencies honestly.

Cacheability is only as good as the least-cacheable dependency

Here is the trap, and it is the one thing from this chapter to carry away. Effective cacheability propagates from dependencies. A resource is cacheable only if everything it is made from is; join one uncacheable source into an otherwise-cached resource and the cached resource silently stops being cached — every read recomputes, forever. Nothing in the types changes and every test can still pass; the only signal is that reads got slower.

So adding a source to a cached resource is a performance change even when it is a correctness no-op. The remedy is twofold: the kernel will tell you why an answer was not cached (it names the uncacheable dependency rather than leaving you to guess), and the discipline is to join mutable data into a resource that is already uncacheable, not into the expensive cached one. Measure a hot read before and after you add a source to it; that difference is the only thing that will warn you.

Relatedly: a refusal is never cached. A resource that refuses bad input by erroring makes everything that reads it recompute on every read. Where “this input is wrong” is a legitimate answer, return it as a value the caller can see, not as an error — so the cache can keep it.

The cache is also a confidentiality boundary

The quietest crossover in the whole book is here. The cache keys on the capability fingerprint — the principal the answer was computed for. So the index that makes repetition cheap is the same index that keeps one person’s answer from ever being served to another: there is no cache entry reachable across principals. Performance and isolation are one mechanism. You cannot accidentally leak a cached answer across a tenancy boundary, because a cached answer computed under one authority is not addressable under a different one.

The throughput reading of the governors

The last chapter framed the overlays as governance — constraints made explicit and enforceable. Caching and golden threads are the throughput half of the same theory-of-constraints picture. The bottleneck in a resolution is the expensive computation; the cache exploits it by reclaiming a prior result at near-zero cost, the golden thread keeps that exploitation honest by cutting the cache exactly when it must, and the scheduler subordinates everything else to the constraint by parking work rather than blocking on it. One drum-buffer-rope picture, drawn once: the governors make the constraint explicit, and the cache spends it wisely.

Presence and the audit axis

Two patterns close the loop the rest of the book opened: the one that puts a person in the loop at the moment of a consequential action, and the one that keeps the record of what was done. They are the human-in-the-loop and its provenance — the front and the back of a single accountable action — and both are drawn here as pictures, because a real passkey ceremony and a real on-disk log need parts of the system a page cannot stand in for.

Presence: a prompt that is a proof

Some actions should require a person, present, right now — not a session that logged in an hour ago and not an agent acting on their behalf. Closing a release, approving a payment, merging a change: these want presence, a capability scope that is satisfied only by a fresh, person-only assertion (a Touch-ID or security-key gesture), minted for this request and never held by a session or named in a stored grant.

   person            the button                     the door
     │          "Close #2 — confirm"                   │
     │ ───────────────────────────────────────────────▶│  mint a challenge bound to the action:
     │                                                  │     nonce  ‖  H( verb ‖ target IRI ‖ digest(content) )
     │ ◀─────────── Touch-ID prompt names the action ───│
     │  gesture                                         │
     │ ───────────────────────────────────────────────▶│  verify the assertion over THAT challenge
     │                                                  │  → mint a one-request presence scope
     │                                                  ▼
     │                                          resolve the close, now

Read it twice, as before. The security property is that the challenge is bound to the exact action — the verb, the target, and a digest of the content — so a captured assertion cannot be replayed against a different action: you cannot approve “close #2” and have it count as “close #7,” or as the same close over different content. The UX property is that the prompt names exactly what you are confirming, in words. Those are the same binding. A refusal (you declined, or the gesture failed) is a Denied — the action does not happen — never a success page that quietly did nothing.

Presence does not cross a network mount by default: a peer cannot assert your presence. That is the right default, and it is the same “authority does not widen” rule the spine was built on.

The audit axis: provenance as a first-class record

Every consequential action leaves a line on the system’s time axis — an append-only, ordered, tamper-evident log. Each entry carries who (the principal, stamped by the door, not claimed by the caller), what, and when, and the entries form a provenance graph: this answer was attributed to that principal, derived from those sources, at that time.

This is primarily a security pattern — accountability, non-repudiation, the ability to ask “what happened, and on whose authority” after the fact. But it has a plain UX surface. The “last used” on a devices-and-sessions page is a read of this log. Provenance shown beside an answer is citation — an agent that tells you where its conclusion came from is reading its own trail. The uniform per-request line a door writes is both an audit record and the raw material for every “recent activity” view a person ever sees.

The principal column is what ties the axis back to the spine: because identity is minted once at a door and reaches every endpoint, the log can attribute each line to the one principal responsible, rather than flattening a whole process into “the server did it.” Attribution is per request, the way authority is.

What was drawn, and what runs elsewhere

The passkey ceremony above can run for real in a browser that has a platform authenticator, but the downstream enforcement — a door minting a one-request scope and a resolution consuming it — needs the door, which is why this chapter is a picture. The hash-chained log is on disk, across rotations, which a page has no file system for. Both are real in a deployment and drawn here, labeled as drawn, with the same promise the overlay chapter made: a runnable version follows as the tooling to drive it lands.

The thesis, once more

You have now seen the worked item from every side. The grant you narrowed is the grant a consent screen renders. The manifold you watched shrink is the action list a page draws and the tool list an agent is handed. The door that would serve the item is the one place that authenticates and the one place that escapes. The cache that made a second read cheap is the boundary that keeps your answer yours. The governors that would make it resilient are constraints made explicit. And the presence that gates its riskiest action is a prompt that is a proof, written into a provenance trail attributed to you.

None of those was a security feature added to a product. Each was a property of resolving a name under a capability — the one mechanism this book has turned to the light, chapter by chapter, until both of its faces were in view at once. That is the ikigai architecture: not a UX stack and a security stack, but one substrate, read two ways.

The other books

This book builds on The ikigai Book, which teaches resources, spaces, capabilities and golden threads from the first endpoint up, and on its chapters on dynamic spaces in particular, which are the mechanism the overlay chain here is built from.

The worked resource throughout — the ledger item — is the subject of The gonk Book, the work ledger and review host built from those same pieces; the kernel this book’s cells run against is that ledger, compiled to WebAssembly. Building ikigai UIs takes the “a view is a resource” idea this book leans on and builds a real inspector with it. Running ikigai is the operator’s side: the ikigai binary that serves the door and the capabilities this book describes, and how to run it for real.

The library lists all five, with who each is for, what to read first and where to start.

The books are built, tested and published together, from one repository: https://github.com/ikigai-rs/ikigai-tutorial.