Urbit Docs
  • What is Urbit?
  • Get on Urbit
  • Build on Urbit
    • Contents
    • Environment Setup
    • Hoon School
      • 1. Hoon Syntax
      • 2. Azimuth (Urbit ID)
      • 3. Gates (Functions)
      • 4. Molds (Types)
      • 5. Cores
      • 6. Trees and Addressing
      • 7. Libraries
      • 8. Testing Code
      • 9. Text Processing I
      • 10. Cores and Doors
      • 11. Data Structures
      • 12. Type Checking
      • 13. Conditional Logic
      • 14. Subject-Oriented Programming
      • 15. Text Processing II
      • 16. Functional Programming
      • 17. Text Processing III
      • 18. Generic and Variant Cores
      • 19. Mathematics
    • App School I
      • 1. Arvo
      • 2. The Agent Core
      • 3. Imports and Aliases
      • 4. Lifecycle
      • 5. Cards
      • 6. Pokes
      • 7. Structures and Marks
      • 8. Subscriptions
      • 9. Vanes
      • 10. Scries
      • 11. Failure
      • 12. Next Steps
      • Appendix: Types
    • App School II (Full-Stack)
      • 1. Types
      • 2. Agent
      • 3. JSON
      • 4. Marks
      • 5. Eyre
      • 6. React app setup
      • 7. React app logic
      • 8. Desk and glob
      • 9. Summary
    • Core Academy
      • 1. Evaluating Nock
      • 2. Building Hoon
      • 3. The Core Stack
      • 4. Arvo I: The Main Sequence
      • 5. Arvo II: The Boot Sequence
      • 6. Vere I: u3 and the Serf
      • 7. Vere II: The Loom
      • 8. Vanes I: Behn, Dill, Kahn, Lick
      • 9. Vanes II: Ames
      • 10. Vanes III: Eyre, Iris
      • 11. Vanes IV: Clay
      • 12. Vanes V: Gall and Userspace
      • 13. Vanes VI: Khan, Lick
      • 14. Vanes VII: Jael, Azimuth
    • Runtime
      • U3
      • Conn.c Guide
      • How to Write a Jet
      • API Overview by Prefix
      • C in Urbit
      • Cryptography
      • Land of Nouns
    • Tools
      • Useful Links
      • JS Libraries
        • HTTP API
      • Docs App
        • File Format
        • Index File
        • Suggested Structure
    • Userspace
      • Command-Line App Tutorial
      • Remote Scry
      • Unit Tests
      • Software Distribution
        • Software Distribution Guide
        • Docket File
        • Glob
      • Examples
        • Building a CLI App
        • Debugging Wrapper
        • Host a Website
        • Serving a JS Game
        • Ship Monitoring
        • Styled Text
  • Urbit ID
    • What is Urbit ID?
    • Azimuth Data Flow
    • Life and Rift
    • Urbit HD Wallet
    • Advanced Azimuth Tools
    • Custom Roller Tutorial
    • Azimuth.eth Reference
    • Ecliptic.eth Reference
    • Layer 2
      • L2 Actions
      • L2 Rollers
      • L2 Roller HTTP RPC-API
      • L2 Transaction Format
  • Urbit OS
    • What is Urbit OS?
    • Base
      • Hood
      • Threads
        • Basics Tutorial
          • Bind
          • Fundamentals
          • Input
          • Output
          • Summary
        • HTTP API Guide
        • Spider API Reference
        • Strandio Reference
        • Examples
          • Child Thread
          • Fetch JSON
          • Gall
            • Poke Thread
            • Start Thread
            • Stop Thread
            • Take Facts
            • Take Result
          • Main-loop
          • Poke Agent
          • Scry
          • Take Fact
    • Kernel
      • Arvo
        • Cryptography
        • Move Trace
        • Scries
        • Subscriptions
      • Ames
        • Ames API Reference
        • Ames Cryptography
        • Ames Data Types
        • Ames Scry Reference
      • Behn
        • Behn API Reference
        • Behn Examples
        • Behn Scry Reference
      • Clay
        • Clay API Reference
        • Clay Architecture
        • Clay Data Types
        • Clay Examples
        • Clay Scry Reference
        • Filesystem Hierarchy
        • Marks
          • Mark Examples
          • Using Marks
          • Writing Marks
        • Using Clay
      • Dill
        • Dill API Reference
        • Dill Data Types
        • Dill Scry Reference
      • Eyre
        • EAuth
        • Eyre Data Types
        • Eyre External API
        • Eyre Internal API
        • Eyre Scry Reference
        • Low-Level Eyre Guide
        • Noun channels
      • Gall
        • Gall API Reference
        • Gall Data Types
        • Gall Scry Reference
      • Iris
        • Iris API Reference
        • Iris Data Types
        • Iris Example
      • Jael
        • Jael API Reference
        • Jael Data Types
        • Jael Examples
        • Jael Scry Reference
      • Khan
        • Khan API Reference
        • Khan Data Types
        • Khan Example
      • Lick
        • Lick API Reference
        • Lick Guide
        • Lick Examples
        • Lick Scry Reference
  • Hoon
    • Why Hoon?
    • Advanced Types
    • Arvo
    • Auras
    • Basic Types
    • Cheat Sheet
    • Cryptography
    • Examples
      • ABC Blocks
      • Competitive Programming
      • Emirp
      • Gleichniszahlenreihe
      • Islands
      • Luhn Number
      • Minimum Path Sum
      • Phone Letters
      • Restore IP
      • Rhonda Numbers
      • Roman Numerals
      • Solitaire Cipher
      • Water Towers
    • Generators
    • Hoon Errors
    • Hoon Style Guide
    • Implementing an Aura
    • Irregular forms
    • JSON
    • Limbs and wings
      • Limbs
      • Wings
    • Mips (Maps of Maps)
    • Parsing Text
    • Runes
      • | bar · Cores
      • $ buc · Structures
      • % cen · Calls
      • : col · Cells
      • . dot · Nock
      • / fas · Imports
      • ^ ket · Casts
      • + lus · Arms
      • ; mic · Make
      • ~ sig · Hints
      • = tis · Subject
      • ? wut · Conditionals
      • ! zap · Wild
      • Constants (Atoms and Strings)
      • --, == · Terminators
    • Sail (HTML)
    • Serialization
    • Sets
    • Standard Library
      • 1a: Basic Arithmetic
      • 1b: Tree Addressing
      • 1c: Molds and Mold-Builders
      • 2a: Unit Logic
      • 2b: List Logic
      • 2c: Bit Arithmetic
      • 2d: Bit Logic
      • 2e: Insecure Hashing
      • 2f: Noun Ordering
      • 2g: Unsigned Powers
      • 2h: Set Logic
      • 2i: Map Logic
      • 2j: Jar and Jug Logic
      • 2k: Queue Logic
      • 2l: Container from Container
      • 2m: Container from Noun
      • 2n: Functional Hacks
      • 2o: Normalizing Containers
      • 2p: Serialization
      • 2q: Molds and Mold-Builders
      • 3a: Modular and Signed Ints
      • 3b: Floating Point
      • 3c: Urbit Time
      • 3d: SHA Hash Family
      • 3e: AES encryption (Removed)
      • 3f: Scrambling
      • 3g: Molds and Mold-Builders
      • 4a: Exotic Bases
      • 4b: Text Processing
      • 4c: Tank Printer
      • 4d: Parsing (Tracing)
      • 4e: Parsing (Combinators)
      • 4f: Parsing (Rule-Builders)
      • 4g: Parsing (Outside Caller)
      • 4h: Parsing (ASCII Glyphs)
      • 4i: Parsing (Useful Idioms)
      • 4j: Parsing (Bases and Base Digits)
      • 4k: Atom Printing
      • 4l: Atom Parsing
      • 4m: Formatting Functions
      • 4n: Virtualization
      • 4o: Molds
      • 5a: Compiler Utilities
      • 5b: Macro Expansion
      • 5c: Compiler Backend & Prettyprinter
      • 5d: Parser
      • 5e: Molds and mold builders
      • 5f: Profiling support
    • Strings
    • The Engine Pattern
    • Udon (Markdown-esque)
    • Vases
    • Zuse
      • 2d(1-5): To JSON, Wains
      • 2d(6): From JSON
      • 2d(7): From JSON (unit)
      • 2e(2-3): Print & Parse JSON
      • 2m: Ordered Maps
  • Nock
    • What is Nock?
    • Decrement
    • Definition
    • Fast Hints and Jets
    • Implementations
    • Specification
  • User Manual
    • Contents
    • Running Urbit
      • Cloud Hosting
      • Home Servers
      • Runtime Reference
      • Self-hosting S3 Storage with MinIO
    • Urbit ID
      • Bridge Troubleshooting
      • Creating an Invite Pool
      • Get an Urbit ID
      • Guide to Factory Resets
      • HD Wallet (Master Ticket)
      • Layer 2 for planets
      • Layer 2 for stars
      • Proxies
      • Using Bridge
    • Urbit OS
      • Basics
      • Configuring S3 Storage
      • Dojo Tools
      • Filesystem
      • Shell
      • Ship Troubleshooting
      • Star and Galaxy Operations
      • Updates
Powered by GitBook

GitHub

  • Urbit ID
  • Urbit OS
  • Runtime

Resources

  • YouTube
  • Whitepaper
  • Awesome Urbit

Contact

  • X
  • Email
  • Gather
On this page
  • The ten arms
  • State management
  • Request handlers
  • Response handlers
  • Scry handler
  • Failure handler
  • Bowl
  • State
  • Transition function
  • Virtualization
  • Example
  • Summary
  • Exercises
Edit on GitHub
  1. Build on Urbit
  2. App School I

2. The Agent Core

In this lesson we'll look at the basic type and structure of a Gall agent.

A Gall agent is a door with exactly ten arms. Each arm is responsible for handling certain kinds of events that Gall feeds in to the agent. A door is just a core with a sample - it's made with the barcab rune (|_) instead of the usual barcen rune (|%).

The ten arms

We'll discuss each of the arms in detail later. For now, here's a quick summary. The arms of an agent can be be roughly grouped by purpose:

State management

These arms are primarily for initializing and upgrading an agent.

  • +on-init: Handles starting an agent for the first time.

  • +on-save: Handles exporting an agent's state - typically as part of the upgrade process but also when suspending, uninstalling and debugging.

  • +on-load: Handles loading a previously exported agent state - typically as part of the upgrade process but also when resuming or reinstalling an agent.

Request handlers

These arms handle requests initiated by outside entities, e.g. other agents, HTTP requests from the front-end, etc.

  • +on-poke: Handles one-off requests, actions, etc.

  • +on-watch: Handles subscription requests from other entities.

  • +on-leave: Handles unsubscribe notifications from other, previously subscribed entities.

Response handlers

These two arms handle responses to requests our agent previously initiated.

  • +on-agent: Handles request acknowledgements and subscription updates from other agents.

  • +on-arvo: Handles responses from vanes.

Scry handler

  • +on-peek: Handles local read-only requests.

Failure handler

  • +on-fail: Handles certain kinds of crash reports from Gall.

Bowl

The sample of a Gall agent door is always a $bowl:gall. Every time an event triggers the agent, Gall populates the bowl with things like the current date-time, fresh entropy, subscription information, which ship the request came from, etc, so that all the arms of the agent have access to that data. For the exact structure and contents of the bowl, have a read through its entry in the Gall vane types documentation.

One important thing to note is that the bowl is only repopulated when there's a new Arvo event. If a local agent or web client were to send multiple messages to your agent at the same time, these would all arrive in the same event. This means if your agent depended on a unique date-time or entropy to process each message, you could run into problems if your agent doesn't account for this possibility.

State

If you've worked through Hoon School, you may recall that a core is a cell of [battery payload]. The battery is the core itself compiled to Nock, and the payload is the subject which it operates on.

For an agent, the payload will at least contain the bowl, the usual Hoon and /sys/zuse.hoon standard library functions, and the state of the agent. For example, if your agent were for an address book app, it might keep a +map of ships to address book entries. It might add entries, delete entries, and modify entries. This address book +map would be part of the state stored in the payload.

Transition function

If you recall from the prologue, the whole Arvo operating system works on the basis of a simple transition function (event, oldState) -> (effects, newState). Gall agents also function the same way. Eight of an agent's ten arms produce the same thing, a cell of:

  • Head: A list of effects called $cards (which we'll discuss later).

  • Tail: A new agent core, possibly with a modified payload.

It goes something like this:

  1. An event is routed to Gall.

  2. Gall calls the appropriate arm of the agent, depending on the kind of event.

  3. That arm processes the event, returning a list $cards to be sent off, and the agent core itself with a modified state in the payload.

  4. Gall sends the $cards off and saves the modified agent core.

  5. Rinse and repeat.

Virtualization

When a crash occurs in the kernel, the system usually aborts the computation and discards the event as though it never happened. Gall on the other hand virtualizes all its agents, so this doesn't happen. Instead, when a crash occurs in an agent, Gall intercepts the crash and takes appropriate action depending on the kind of event that caused it. For example, if a poke from another ship caused a crash in the +on-poke arm, Gall will respond to the poke with a "nack", a negative acknowledgement, telling the original ship the poke was rejected.

What this means is that you can intentionally design your agent to crash in cases it can't handle. For example, if a poke comes in with an unexpected $mark, it crashes. If a permission check fails, it crashes. This is quite different to most programs written in procedural languages, which must handle all exceptions to avoid crashing.

Example

Here's about the simplest valid Gall agent:

|_  =bowl:gall
++  on-init   `..on-init
++  on-save   !>(~)
++  on-load   |=(vase `..on-init)
++  on-poke   |=(cage !!)
++  on-watch  |=(path !!)
++  on-leave  |=(path `..on-init)
++  on-peek   |=(path ~)
++  on-agent  |=([wire sign:agent:gall] !!)
++  on-arvo   |=([wire sign-arvo] !!)
++  on-fail   |=([term tang] `..on-init)
--

This is just a dummy agent that does absolutely nothing - it has no state and rejects all messages by crashing. Typically we'd cast this to an +agent:gall, but in this instance we won't so it's easier to examine its structure in the dojo. We'll get to what each of the arms do later. For now, we'll just consider a few particular points.

Firstly, note its structure - it's a door (created with |_) with a sample of $bowl:gall and the ten arms described earlier. The =bowl:gall syntax simply means bowl=bowl:gall ($= irregular syntax).

Secondly, you'll notice some of the arms return:

`..on-init

A backtick at the beginning is an irregular syntax meaning "prepend with null", so for example, in the dojo:

> `50
[~ 50]

The next part has ..on-init, which means "the subject of the +on-init arm". The subject of the +on-init arm is our whole agent. In the transition function section we mentioned that most arms return a list of effects called $cards and a new agent core. Since an empty list is ~, we've created a cell that fits that description.

Let's examine our agent. In the Dojo of a fake ship, mount the %base desk with |mount %base. On the Unix side, navigate to /path/to/fake/ship/base, and save the above agent in the /app directory as skeleton.hoon. Back in the dojo, commit the file to the desk with |commit %base.

For the moment we won't install our %skeleton agent. Instead, we'll use the -build-file thread to build it and save it in the dojo's subject so we can have a look. Run the following in the dojo:

> =skeleton -build-file %/app/skeleton/hoon

Now, let's have a look:

> skeleton
< 10.fxw
  [   bowl
    [ [our=@p src=@p dap=@tas sap=/]
      [ wex=nlr([p=[wire=/ ship=@p term=@tas] q=[acked=?(%.y %.n) path=/]])
        sup=nlr([p=it(/) q=[p=@p q=/]])
        sky=nlr([p=/ q=nlr([key=@ud val=[p=@da q=?([%.y p=[p=@tas q=*]] [%.n p=@uvI])]])])
      ]
      act=@ud
      eny=@uvJ
      now=@da
      byk=[p=@p q=@tas r=?([%da p=@da] [%tas p=@tas] [%ud p=@ud] [%uv p=@uv])]
    ]
    <15.eah 40.ihi 14.tdo 54.xjm 77.vsv 236.zqw 51.njr 139.oyl 33.uof 1.pnw %138>
  ]
>

The Dojo pretty-prints cores with a format of number-of-arms.hash. You can see the head of %skeleton is 10.fxw - that's the battery of the core, our 10-arm agent. If we try printing the head of %skeleton we'll see it's a whole lot of compiled Nock:

> -.skeleton
[ [ 11
    [ 1.953.460.339
      1
      [ 7.368.801
        7.957.707.045.546.060.659
        1.852.796.776
        0
      ]
      [7 15]
      7
      34
    ]
...(truncated for brevity)...

The battery's not too important, it's not something we'd ever touch in practice. Instead, let's have a look at the core's payload by printing the tail of %skeleton. We'll see its head is the $bowl:gall sample we specified, and then the tail is just all the usual standard library functions:

> +.skeleton
[   bowl
  [ [our=~zod src=~zod dap=%$ sap=/]
    [wex=~ sup=~ sky=~]
    act=0
    eny=0v0
    now=~2000.1.1
    byk=[p=~zod q=%$ r=[%uv p=0v0]]
  ]
  <15.eah 40.ihi 14.tdo 54.xjm 77.vsv 236.zqw 51.njr 139.oyl 33.uof 1.pnw %138>
]

Currently %skeleton has no state, but of course in practice you'd want to store some actual data. We'll add foo=42 as our state with the =+ rune at the beginning of our agent:

=+  foo=42
|_  =bowl:gall
++  on-init   `..on-init
++  on-save   !>(~)
++  on-load   |=(vase `..on-init)
++  on-poke   |=(cage !!)
++  on-watch  |=(path !!)
++  on-leave  |=(path `..on-init)
++  on-peek   |=(path ~)
++  on-agent  |=([wire sign:agent:gall] !!)
++  on-arvo   |=([wire sign-arvo] !!)
++  on-fail   |=([term tang] `..on-init)
--

Save the modified skeleton.hoon in /app on the %base desk like before, and run |commit %base again in the dojo. Then, rebuild it with the same -build-file command as before:

> =skeleton -build-file %/app/skeleton/hoon

If we again examine our agent core's payload by looking at the tail of %skeleton, we'll see foo=42 is now included:

> +.skeleton
[   bowl
  [ [our=~zod src=~zod dap=%$ sap=/]
    [wex=~ sup=~ sky=~]
    act=0
    eny=0v0
    now=~2000.1.1
    byk=[p=~zod q=%$ r=[%uv p=0v0]]
  ]
  foo=42
  <15.eah 40.ihi 14.tdo 54.xjm 77.vsv 236.zqw 51.njr 139.oyl 33.uof 1.pnw %138>
]

Summary

  • A Gall agent is a door with exactly ten specific arms and a sample of $bowl:gall.

  • Each of the ten arms handle different kinds of events - Gall calls the appropriate arm for the kind of event it receives.

  • The ten arms fit roughly into five categories:

    • State management.

    • Request handlers.

    • Response handlers.

    • Scry handler.

    • Failure handler.

  • The state of an agent—the data it's storing—lives in the core's payload.

  • Most arms produce a list of effects called $cards, and a new agent core with a modified state in its payload.

Exercises

  • Run through the Example yourself on a fake ship if you've not done so already.

  • Have a look at the $bowl entry in the Gall data types documentation if you've not done so already.

Previous1. ArvoNext3. Imports and Aliases

Last updated 1 day ago