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
  • Introduction
  • The Shoe library
  • Session identifiers
  • %shoe cards
  • Shoe core
  • +default core
  • +agent core
  • The Sole library
  • %shoe app walkthrough
  • Playing with %shoe
  • %shoe's code
Edit on GitHub
  1. Build on Urbit
  2. Userspace

Command-Line App Tutorial

Introduction

In this walkthrough we will go in-depth on how to build command line interface (CLI) applications in Urbit using /lib/shoe.hoon in the %base desk.

There are three CLI apps that currently ship with Urbit: %dojo, %chat-cli, and %shoe. You should be familiar with the former two, the latter is an example app that shows off how the Shoe library works that we will be looking at closely. These are all Gall apps, and their source can be found in the /app folder of your %base desk.

In the Shoe library we take a closer look at the Shoe library and its cores and how they are utilized in CLI apps. Then in the sole library we look at what Shoe effects ultimately break down into. Finally in %shoe app walkthrough we explore the functionality of the %shoe agent and then go through the code line-by-line.

This tutorial can be considered to be an application equivalent of the Hoon school lesson on %sole and %ask generators, which only covers the bare minimum necessary to write generators that take user input.

The Shoe library

Here we describe how sessions are identified, the specialized $card:shoes that Gall agents with the Shoe library are able to utilize, and the different cores of /lib/shoe.hoon and their purpose.

Session identifiers

An app using the Shoe library will automatically track sessions by their $sole-id. A $sole-id is made up of a "host" ship name and some identifier string ([who=@p ses=@ta]). Generally, however, the $sole-id as a whole should be treated as an opaque identifier, generated by the connecting client. An app using the Shoe library may be connected to by a local or remote ship in order to send commands, and each of these connections is assigned a unique $sole-id that identifies the ship and which session on that ship if there are multiple.

%shoe cards

Gall agents with the Shoe library are able to utilize $card:shoes. These additions to Gall's standard set of $card:agent:galls have the following shape:

/lib/shoe.hoon
[%shoe sole-ids=(list sole-id) effect=shoe-effect]

The Shoe card's sole-ids is the list of session ids that the following $shoe-effect is emitted to. An empty sole-ids list sends the effect to all connected sessions. $shoe-effects, for now, are always of the shape [%sole effect=sole-effect], where $sole-effects are basic console events such as displaying text, changing the prompt, beeping, etc. These are described in the section on the sole library.

For example, a $card:shoe that causes all connected sessions to beep would be [%shoe ~ %sole %bel ~].

Shoe core

An iron (contravariant) door that defines an interface for Gall agents utilizing the Shoe library. Use this core whenever you want to receive input from the user and run a command. The input will get put through the parser (+command-parser) and results in a noun of $command-type that the underlying application specifies, which Shoe then feeds back into the underlying app as an +on-command callback.

In addition to the ten arms that all Gall core apps possess, +shoe defines and expects a few more, tailored to common CLI logic. Thus you will need to wrap the +shoe:shoe core using the +agent:shoe function to obtain a standard 10-arm Gall agent core. See the Shoe example app walkthrough for how to do this.

The additional arms are described below. The Hoon code shows their expected type signature. As we'll see later, the $command-type can differ per application. Note also that most of these take a session identifier as an argument. This lets applications provide different users (at potentially different "places" within the application) with different affordances.

+command-parser

/lib/shoe.hoon
  ++  command-parser
    |~  =sole-id
    |~(nail *(like [? command-type]))

Input parser for a specific command-line session. Will be run on whatever the user tries to input into the command prompt, and won't let them type anything that doesn't parse. If the head of the result is true, instantly run the command. If it's false, require the user to press return.

+tab-list

/lib/shoe.hoon
  ++  tab-list
    |~  =sole-id
    *(list (option:auto tank))

Autocomplete options for the command-line session (to match +command-parser).

+on-command

/lib/shoe.hoon
  ++  on-command
    |~  [=sole-id command=command-type]
    *(quip card _this)

Called when a valid command is run.

+can-connect

/lib/shoe.hoon
  ++  can-connect
    |~  =sole-id
    *?

Called to determine whether a session may be opened or connected to. For example, you may only want the local ship to be able to connect.

+on-connect

/lib/shoe.hoon
  ++  on-connect
    |~  =sole-id
    *(quip card _^|(..on-init))

Called when a session is opened or connected to.

+on-disconnect

/lib/shoe.hoon
  ++  on-disconnect
    |~  =sole-id
    *(quip card _^|(..on-init))

Called when a previously made session gets disconnected from.

+default core

This core contains the bare minimum implementation of the additional Shoe arms beyond the 10 standard Gall app ams. It is used analogously to how the +default agent core is used for regular Gall apps.

+agent core

This is a function for wrapping a Shoe core, which has too many arms to be a valid Gall agent core. This turns it into a standard Gall agent core by integrating the additional arms into the standard ones.

The Sole library

Shoe apps may create specialized $card:shoes of the [%shoe (list sole-id) shoe-effect] shape, where $shoe-effect currently just wrap $sole-effects, i.e. instructions for displaying text and producing other effects in the console.

The list of possible $sole-effects can be found in /sur/sole.hoon. A few commonly used ones are as follows.

  • [%txt tape]: display a line of text.

  • [%bel ~]: emit a beep.

  • [%pro sole-prompt]: set the prompt.

  • [%mor (list sole-effect)]: emit multiple effects.

For example, a $sole-effect that displays This is some text. and beeps would be structured as

[%mor [%txt "This is some text."] [%bel ~] ~]

%shoe app walkthrough

Here we explore the capabilities of the %base desk's example Shoe agent /app/shoe.hoon, and then go through the code, explaining what each line does.

Playing with %shoe

First let's test the functionality of the %shoe agent so we know what we're getting into.

Start two fake ships, one named ~zod. The other may have any name, let's go with ~nus. Fake ships run locally are able to see each other, and our intention is to connect their %shoe apps.

On each fake ship start %shoe by entering |start %shoe into their Dojos. Now that the agents are running, you can run |dojo/link %shoe to connect to the CLI interface. This will change the prompt to ~zod:shoe> and ~nus:shoe>. Type demo and watch the following appear:

Dojo
~zod ran the command
~zod:shoe>

~zod ran the command should be displayed in bold green text, signifying that the command originated locally.

Now we will connect the sessions. Switch ~zod back to dojo by pressing CTRL+x and enter |dojo/link ~nus %shoe. If this succeeds you will see the following.

Dojo
>=
; ~nus is your neighbor
[linked to [p=~nus q=%shoe]]

Now ~zod will have two %shoe sessions running - one local one on ~zod and one remote one on ~nus, which you can access by pressing CTRL+x until you see ~nus:shoe> from ~zod's console. On the other hand, you should not see ~zod:shoe> on ~nus's side, since you have not connected ~nus to ~zod's %shoe agent. When you enter demo from ~nus:shoe> on ~zod's console you will again see ~zod ran the command, but this time it should be in the ordinary font used by the console, signifying that the command is originating from a remote session. Contrast this with entering demo from ~nus:shoe> in ~nus's console, which will display ~nus ran the command in bold green text.

Now try to link to ~zod's %shoe session from ~nus by switching to the dojo on ~nus and entering |dojo/link ~zod %shoe. You should see

Dojo
>=
[unlinked from [p=~zod q=%shoe]]

And if you press CTRL+x you will not get a ~zod:shoe> prompt. This is because the example app is set up to always allow ~zod to connect (as well as subject moons if the ship happens to be a planet) but not ~nus, so this message means that ~nus failed to connect to ~zod's %shoe session.

%shoe's code

/app/shoe.hoon
::  shoe: example usage of /lib/shoe
::
::    the app supports one command: "demo".
::    running this command renders some text on all sole clients.
::
/+  shoe, verb, dbug, default-agent

/+ is the Ford rune which imports libraries from the local desk's /lib directory into the subject.

  • shoe: the Shoe library.

  • verb: a library used to print what a Gall agent is doing.

  • dbug: a library of debugging tools.

  • default-agent: a Gall agent core with minimal implementations of required Gall arms.

/app/shoe.hoon
|%
+$  state-0  [%0 ~]
+$  command  ~
::
+$  card  card:shoe
--

The types used by the app.

$state-0 stores the state of the app, which is ~ (null) as there is no state to keep track of. It is good practice to include a version number anyways, in case the app is made stateful at a later time.

$command is typically a set of tagged union types that represent the possible commands that can be entered by the user. Since this app only supports one command, it is unnecessary for it to have any associated data, thus the command is represented by ~.

In a non-trivial context, a $command is commonly given by a $page ((pair term noun)), where the $term is the identifier for the type of command and the $noun is any type or list of types that contain the data needed to execute the command. See /app/chat-cli.hoon for examples of commands, such as [%say letter:store] and [%delete path]. This is not required though, and you could use something like [chat-room=@t =action].

$card:shoe is either an ordinary $card:agent:gall or a Shoe card, which takes the shape [%shoe sole-ids=(list sole-id) effect=shoe-effect]. A Shoe card is sent to all sessions listed in its sole-ids, making them run its $sole-effect (e.g. printing some text). Here we can reference $card:shoe because of /+ shoe at the beginning of the agent file.

/app/shoe.hoon
=|  state-0
=*  state  -
::

The above adds the bunt (default value) of $state-0 to the head of the subject, then give it the macro state. The - here is a lark expression referring to the head of the subject. This allows us to use state to refer to the state elsewhere in the code no matter what version we're using, while also getting direct access to the contents of state (if it had any).

/app/shoe.hoon
%+  verb  |
%-  agent:dbug
^-  agent:gall
%-  (agent:shoe command)
^-  (shoe:shoe command)

The ^- type casts here are just reminders of what is being produced. So let's focus on what the % runes are doing, from bottom to top. We call the (agent:shoe command) function on what follows (in this case, the rest of the app), producing a standard Gall agent core. Then we call wrap the Gall agent core with +agent:dbug, endowing it with additional arms useful for debugging, and then wrap again with +verb.

/app/shoe.hoon
|_  =bowl:gall
+*  this  .
    def   ~(. (default-agent this %|) bowl)
    des   ~(. (default:shoe this command) bowl)

This is boilerplate Gall agent core code. We set this to be a macro for the subject, which is the Gall agent core itself. We set def and des to be macros for initialized +default-agent and +default:shoe doors respectively.

Next we implement all of the arms required for a Shoe agent. Starting with the standard Gall arms:

/app/shoe.hoon
++  on-init   on-init:def
++  on-save   !>(state)
++  on-load
  |=  old=vase
  ^-  (quip card _this)
  [~ this]
::
++  on-poke   on-poke:def
++  on-watch  on-watch:def
++  on-leave  on-leave:def
++  on-peek   on-peek:def
++  on-agent  on-agent:def
++  on-arvo   on-arvo:def
++  on-fail   on-fail:def

These are minimalist Gall app arm implementations using the default behavior found in def.

Here begins the implementation of the additional arms required by the (shoe:shoe command) function.

/app/shoe.hoon
++  command-parser
  |=  =sole-id
  ^+  |~(nail *(like [? command]))
  (cold [& ~] (jest 'demo'))

+command-parser is of central importance - it is what is used to parse user input and transform it into $commands for the app to execute. Writing a proper command parser requires understanding of the Hoon parsing functions found in the standard library. How to do so may be found in the parsing tutorial. For now, it is sufficient to know that this arm matches the text "demo" and produces a [? command]-shaped noun in response. Note how the & signifies that the command will be run as soon as it has been entered, without waiting for the user to press return.

/app/shoe.hoon
++  tab-list
  |=  =sole-id
  ^-  (list [@t tank])
  :~  ['demo' leaf+"run example command"]
  ==

+tab-list is pretty much plug-and-play. For each command you want to be tab completed, add an entry to the list begun by :~ of the form [%command leaf+"description"]. Now whenever the user types a partial command and presses tab, the console will display the list of commmands that match the partial command as well as the descriptions given here.

Thus here we have that starting to type demo and pressing tab will result in the following output in the console...

Dojo
demo  run example command
~zod:shoe> demo

...with the remainder of demo now added to the input line.

Next we have +on-command, which is called whenever +command-parser recognizes that demo has been entered by a user.

/app/shoe.hoon
++  on-command
  |=  [=sole-id =command]
  ^-  (quip card _this)

This is a gate that takes in the $sole-id corresponding to the session and the command noun parsed by +command-parser and returns a list of $card:shoes and _this, which is our shoe agent core including its state.

/app/shoe.hoon
  =-  [[%shoe ~ %sole -]~ this]

This creates a cell of a $card:shoe that triggers a $sole-effect given by the head of the subject -, then the Gall agent core this - the return result of this gate. The use of the =- rune means that what follows this expression is actually run first, which puts the desired $sole-effect into the head of the subject.

/app/shoe.hoon
  =/  =tape  "{(scow %p src.bowl)} ran the command"

We define the $tape that we want to be printed.

/app/shoe.hoon
  ?.  =(src our):bowl
    [%txt tape]
  [%klr [[`%br ~ `%g] [(crip tape)]~]~]

We cannot just produce the $tape we want printed, - it needs to fit the $sole-effect type. This tells us that if the origin of the command is not our ship to just print it normally with the %txt $sole-effect. Otherwise we use %klr, which prints it stylistically (here it makes the text green and bold).

The following allows either ~zod, or the host ship and its moons, to connect to this app's command line interface using |dojo/link.

/app/shoe.hoon
++  can-connect
  |=  =sole-id
  ^-  ?
  ?|  =(~zod src.bowl)
      (team:title [our src]:bowl)
  ==

We use the minimal implementations for the final two Shoe arms, since we don't want to do anything special when users connect or disconnect.

/app/shoe.hoon
++  on-connect      on-connect:des
++  on-disconnect   on-disconnect:des
--

This concludes our review of the code of the %shoe app.

To continue learning how to build your own CLI app, we recommend checking out %chat-cli.

PreviousUserspaceNextRemote Scry

Last updated 1 day ago