14. Vanes VII: Jael, Azimuth

Azimuth

Azimuth is the “Urbit ID” component of the system. It allows ships to securely communicate over Ames as peers, either using galaxy-based routing (today) or a direct route (if known from previous contact).

Each ship in a two-way conversation computes the shared symmetric key for that conversation by computing the product of their own private key and the public key of the other party.

Azimuth is a “public key infrastructure“, meaning that binds public keys to entity identities, with each entity holding its own private key. For each point, Azimuth needs to know three things:

  1. Identity (point)
  2. Life (key revision number)
  3. Rift (continuity number)

These values are utilized by Ames and Jael to ensure that communication between ships is always done with the most recent set of networking keys, and that networking state is appropriately reset when a breach has occurred.

Today, Azimuth is instantiated as an NFT, technically an ERC-721 token. The original Azimuth was “Layer 1” on Ethereum, meaning that the transactions and Azimuth state are directly verified by the Ethereum Virtual Machine. More recently, a “Layer 2” was added, which permits batched transactions very cheaply at the cost of eliding the EVM check (and external visibility).

Azimuth is strictly under the governance of the Galactic Senate and so the Ethereum state could be overridden. “Sovereign is he who decides the exception” (Carl Schmitt), and this provides an explicit safety valve against a chain state or contract exploit against Ethereum. (This would be controversial, I imagine, and it could break Azimuth-on-Ethereum, but there are circumstances under which it would make sense.)

The Senate has no ability to touch this data directly. This is in direct contrast to all existing centralized services, where your account is always at risk of being taken away from you. What the Senate can change is the “business logic” that decides how you can interact with the data in Azimuth. These are mechanisms such as what powers various proxies have, how stars/planets are released over time, and how sponsorship works.

We will not discuss details of Layer 2 points other than to note that from Jael's perspective they behave the same as L1 points.

Identity

You are doubtless familiar with the fivefold hierarchical nature of Urbit ID:

TypeSpanNumber
Galaxy0x00xff256
Star0x1000xffff65.280
Planet0x1.00000xffff.ffff4.294.901.760
Moon0x1.0000.00000xffff.ffff.ffff.ffff$2{64}-2{32}$
Comet0x1.0000.0000.0000.00000xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff$2{128}-2{64}$

(The counts are not generally even powers of two because of the carveout for the superior points.)

At various points in the past, these points had different names:

Type**Feudal Type I ** (fort)Feudal Type II (fort)Maritime Type (ship)
Galaxydukeczarcarrier
Starearlkingcruiser
Planetlorddukedestroyer
Moonpawnearlyacht
Cometwolfpawnsubmarine

(~zod was originally pope in the feudal terminology.)

Within Arvo, these are still baked into the core rank types, so you need occasionally to know the second set of feudal types:

+$ rank ?(%czar %king %duke %earl %pawn) :: ship width class
++ clan :: ship to rank
|= who=ship
^- rank
=/ wid (met 3 who)
?: (lte wid 1) %czar
?: =(2 wid) %king
?: (lte wid 4) %duke
?: (lte wid 8) %earl
?> (lte wid 16) %pawn
> (sein:title our now our)
~zod
> (clan:title our)
%czar

The ++title core in /sys/zuse provides identity-related functions. Saliently:

  • ++sein for the actual sponsor of a particular point. (We distinguish because of emancipation.)
  • ++clan for the rank.
  • ++name for the responsible point (that point unless a moon).
  • ++saxo for the on-Azimuth sponsorship chain.
> (saxo:title our now ~middev-middev)
[i=~middev-middev t=[i=~nodsel t=~[~sel]]]

Galaxies

Galaxies have a public key visible in Azimuth PKI. Their IP address is loaded on boot from a galaxy table supplied by Urbit (as organization). Then each ship can build the sponsorship chain it needs by construction.

Stars, Planets

Other points’ public keys in Azimuth PKI are obtained from Ethereum and stored in Jael, along with its sponsor, life, and rift.

Moons

Moons do not have an independent Azimuth presence, but rely on their parent ship's Jael to store their life and rift values.

Comets

Comets do not have a registration in Azimuth. They cannot cycle their networking keys, and they cannot breach, so their life and rift are always 0.

Ecliptic

There are two contract addresses for Azimuth:

  • Azimuth.eth is the contract data store containing the actual state of Azimuth on Ethereum.
  • Ecliptic.eth contains the operational logic (transfers, unlocking, etc.). It is separate from Azimuth.eth so that the contracts can be upgraded without losing chain state.

Ecliptic contracts are written in Solidity, the EVM contract programming language.

  • Azimuth: contains all on-chain state for azimuth. Most notably, ownership and public keys. Can't be modified directly, you must use the Ecliptic.
  • Ecliptic: is used as an interface for interacting with your points on-chain. Allows you to configure keys, transfer ownership, etc.
  • Polls: registers votes by the Galactic Senate on proposals. These can be either static documents or Ecliptic upgrades.
  • Linear Star Release: facilitates the release of blocks of stars to their owners over a period of time.
  • Conditional Star Release: facilitates the release of blocks of stars to their owners based on milestones.
  • Claims: allows point owners to make claims about (for example) their identity, and associate that with their point.
  • Censures: simple reputation management, allowing galaxies and stars to flag points for negative reputation.
  • Delegated Sending: enables network-effect like distributing of planets.
  • Planet Sale: gives an example of a way in which stars could sell planets on-chain.

There is no limit for galaxies. Instead, for most galaxies, all stars have already been spawned and placed into one of the lockup contracts: Linear Star Release↗ and Conditional Star Release↗. Beginning in 2019, stars may spawn at most 1024 planets. This limit doubles every subsequent year until the maximum is reached.

Bridge

Bridge is the primary interface to execute Ecliptic contracts.

Urbit HD Wallet

A hierarchical deterministic (HD) wallet is a system of related key pairs with distinct delegated powers, à la BIP-32. A master key pair is used to deterministically generate subsidiary key pairs with specific capabilities on the chain.

As a point owner, you receive some means of claiming the point initially—a claim code or email or simply a transfer to an Ethereum address that you must accept. From that, you can either hold the point via the ownership address on Ethereum or via the master ticket (with seed @q).

(A @q is like a @p in that it is syllabic, but has no limitation on value or range unlike that implied by @p.)

The holder of the main ownership address and key pair can designate proxies who can only run certain Ecliptic transactions.

The key pairs are:

  • Ownership
  • Transfer Proxy (set on a temporary basis to make transferring a point a two-step process less prone to error)
  • Spawn Proxy (only galaxies and stars; can spawn new child identities)
  • Management Proxy (planets, stars, and galaxies; can set life and rift, and manage sponsorship; receives boot keyfile)
  • Voting Proxy (only galaxies)

The /app/claz command-line tool lets you perform specific sophisticated actions on L1 points from a fake ship. This provides an unmediated interface to Ecliptic.

  • Read through /app/claz, /ted/claz/prep-command, and in particular /lib/claz.

Like sudo, this is riskier but direct.

Breaching/Factory Resets

What is a breach?

Ships on the Ames network sometimes need to reset their continuity. A factory reset (hereafter just called a reset) is when an individual ship announces to the network: "I forgot who I am, let's start over from scratch." That is, it clears its own event log and sends an announcement to the network, asking all ships that have communicated with it to reset its networking information in their state. This makes it as though the ship was just started for the first time again, since everyone on the network has forgotten about it.

We call them “factory resets” for the public, but internally it's all %breach.

  • See /sys/vane/ames:++on-publ-breach.

Since a breach happens to a ship, it cannot know about its own breach.

Jael

Jael stores Azimuth information. /sys/vane/jael is intimately related to Azimuth. Azimuth represents what we may call the exoteric view of ownership, while Jael is the esoteric view. Jael’s primary role is ship networking management and cryptography, but it also supports promises.

/sys/vane/jael segregates state into two categories: absolute and relative. Absolute state refers to what is known about the Azimuth PKI, ship ownership, private keys, etc. Since not every ship is live on the network (such as a fakezod), there is also a notion of relative state, referring to what is known about the current ship only.

Jael's code has Azimuth hardcoded into it, but could be made to talk to a different agent pretty easily, as long as it communicates key change & sponsorship events in the same way.

/sys/lull Definition

Jael defines a lot of different types to support what different parts of the system need to see. (For instance, /app/eth-watcher needs to communicate with /sys/vane/jael.)

Tasks and Gifts

+$ gift :: out result <-$
$% [%done error=(unit error:ames)] :: ames message (n)ack
[%boon payload=*] :: ames response
::
[%private-keys =life vein=(map life ring)] :: private keys
[%public-keys =public-keys-result] :: ethereum changes
::
[%turf turf=(list turf)] :: domains
== ::
+$ task :: in request ->$
$~ [%vega ~] ::
$% [%dawn dawn-event] :: boot from keys
[%fake =ship] :: fake boot
::
[%listen whos=(set ship) =source] :: set ethereum source
[%meet =ship =life =pass] :: met after breach
[%nuke whos=(set ship)] :: cancel tracker from
[%ruin ships=(set ship)] :: pretend breach
::
[%private-keys ~] :: sub to privates
[%public-keys ships=(set ship)] :: sub to publics
[%rekey =life =ring] :: update private keys
[%resend ~] :: resend private key
::
[%turf ~] :: view domains
::
[%moon =ship =udiff:point] :: register moon keys
[%step ~] :: reset web login code
::
$>(%trim vane-task) :: trim state
$>(%vega vane-task) :: report upgrade
$>(%plea vane-task) :: ames request
== ::
  • %private-keys are used by Ames.
  • %public-keys are used in a few places where messages need to be decrypted, e.g. Clay and /app/ping.
  • %turf relates to domains for real ships.

Organization

/sys/vane/jael is not a particularly complicated vane.

lex is the durable state of Jael:

=| lex=state-2
+$ state-2
$: %2
pki=state-pki-2 ::
etn=state-eth-node :: eth connection state
== ::

+$state-pki is the known PKI state:

+$ state-pki-2 :: urbit metadata
$: $= own :: vault (vein)
$: yen=(set duct) :: trackers
sig=(unit oath) :: for a moon
tuf=(list turf) :: domains
fak=_| :: fake keys
lyf=life :: version
step=@ud :: login code step
jaw=(map life ring) :: private keys
== ::
$= zim :: public
$: yen=(jug duct ship) :: trackers
ney=(jug ship duct) :: reverse trackers
nel=(set duct) :: trackers of all
dns=dnses :: on-chain dns state
pos=(map ship point) :: on-chain ship state
== ::
==

+$state-eth-node is the state of a connexion to an Ethereum node.

+$ source (each ship term)
+$ source-id @udsourceid
::
:: +state-eth-node: state of a connection to an ethereum node
::
+$ state-eth-node :: node config + meta
$: top-source-id=source-id
sources=(map source-id source)
sources-reverse=(map source source-id)
default-source=source-id
ship-sources=(map ship source-id)
ship-sources-reverse=(jug source-id ship)
== ::

There are a number of ancillary cores that track the public-key/private-key state.

For the most part, you won't need to directly use Jael when working with Gall agents unless you are carrying out Azimuth operations.

There is a standard vane interface core which dispatches to an engine.

The main body of /sys/vane/jael has several engines:

  • ++ez Ethereum wallet algebra
  • ++of main engine for top-level semantics.
  • ++su subjective engine for all derived state, subscriptions, and actions.

The ++feel core handles tracking and updating public keys.

  • Trace an Azimuth update from %fact via ++new-event to ++feel's operations.

Parenthetically, /sys/vane/jael uses a unique pattern for invoking ++abet:

%- curd =< abet
(private-keys:~(feel su hen now pki etn) life.tac ring.tac)

Scries

Jael takes no cares, so all information is exposed via the path. Oddly, rather than a desk name, Jael expects an operation type. (This is a very old scry pattern and should be cleaned up.)

Reveal your web login code:

.^(@p %j /=code=/(scot %p our))

Query who the ship's sponsor is:

.^(@p %j /=sein=/~sampel-palnet)

Query the sponsorship chain:

.^((list @p) %j /=saxo=/~sampel-palnet)

Get current state of subscriptions to public key updates; this won't have much to say if you are on a fakeship:

.^([yen=(jug duct ship) ney=(jug ship duct) nel=(set duct)] %j /=subscriptions=/1)

Azimuth Data Flow

Jael does not talk to Ethereum/Azimuth directly. It derives its knowledge of Azimuth as a PKI from some particular source. In the default case, this is /app/eth-watcher and friends.

By default, Jael's primary source of information is the Gall agent %azimuth, which (using %eth-watcher) runs a thread that polls an Ethereum node for transactions in the Azimuth contract. It can also get updates from other ships on the network. (This is always the case for moons, where it subscribes to the moon's parent's Jael for updates.)

/app/azimuth is the agent responsible for subscribing to Azimuth transactions from /app/eth-watcher. (It unrolls L2 transactions as well.) It exposes this data via scries and subscription endpoints.

/app/eth-watcher is actually a generic Ethereum state listener. It works for Azimuth because what Jael needs is conveyed via /app/azimuth.

A few relevant types, eliding the point algebra:

:: ::::
:::: ++jael :: (1h) security
:: ::::
++ jael ^?
|%
+$ public-keys-result
$% [%full points=(map ship point)]
[%diff who=ship =diff:point]
[%breach who=ship]
==
::
++ block
=< block
|%
+$ hash @uxblockhash
+$ number @udblocknumber
+$ id [=hash =number]
+$ block [=id =parent=hash]
--
::
++ point
=< point
|%
+$ point
$: =rift
=life
keys=(map life [crypto-suite=@ud =pass])
sponsor=(unit @p)
==
--
-- :: jael

/sys/lull Shared Types

:: ::::
:::: ++ethereum-types :: eth surs for jael
:: ::::
++ ethereum-types
|%
++ address @ux :: ethereum address, 20 bytes.
+$ event-id [block=@ud log=@ud] :: event location
++ events (set event-id)
--
:: ::::
:::: ++azimuth-types :: az surs for jael
:: ::::
++ azimuth-types
=, ethereum-types
|%
++ point
$: :: ownership
$= own
$: owner=address
management-proxy=address
voting-proxy=address
transfer-proxy=address
==
::
:: networking
$= net
%- unit
$: =life
=pass
continuity-number=@ud
sponsor=[has=? who=@p]
escape=(unit @p)
==
::
:: spawning
$= kid
%- unit
$: spawn-proxy=address
spawned=(set @p)
==
==
::
+$ dnses [pri=@t sec=@t ter=@t]
::
++ diff-azimuth
$% [%point who=@p dif=diff-point]
[%dns dnses]
==
::
++ diff-point
$% [%full new=point] ::
[%owner new=address] :: OwnerChanged
[%activated who=@p] :: Activated
[%spawned who=@p] :: Spawned
[%keys =life =pass] :: ChangedKeys
[%continuity new=@ud] :: BrokeContinuity
[%sponsor new=[has=? who=@p]] :: EscapeAcc/LostSpons
[%escape new=(unit @p)] :: EscapeReq/Can
[%management-proxy new=address] :: ChangedManagementPro
[%voting-proxy new=address] :: ChangedVotingProxy
[%spawn-proxy new=address] :: ChangedSpawnProxy
[%transfer-proxy new=address] :: ChangedTransferProxy
==
--
:: ::
:::: ++pki:jael :: (1h2) certificates
:: ::::
++ pki ^?
|%
+$ hand @uvH :: 128-bit hash
+$ mind [who=ship lyf=life] :: key identifier
+$ name (pair @ta @t) :: ascii / unicode
+$ oath @ :: signature
++ tale :: urbit-signed *
|$ [typ] :: payload mold
$: dat=typ :: data
syg=(map ship (pair life oath)) :: signatures
== ::
-- :: pki
  • What is a change on Azimuth? (See ++diff-point.)

Bootstrapping

Jael rises early in the booting of a new ship, since we bake identity into Arvo very soon. This results from %dawn task passed with a +$dawn-event:

+$ dawn-event
$: =seed
spon=(list [=ship point:azimuth-types])
czar=(map ship [=rift =life =pass])
turf=(list turf)
bloq=@ud
node=(unit purl:eyre)
==

Jael is a landlocked vane, but does receive a %dawn event constructed by vere/dawn.c:u3_dawn_vent().

  • Review that function and _dawn_post_json() for the actual information retrieval.

Example

  • Boot a comet using a star as its sponsor if you have a running star. (Maybe try with the next option.)
  • Boot a fake comet. (See vere/main.c and override.)
  • Build a fake sponsorship chain. Can you change the sponsorship chain to another point (emancipate) on a fakenet? Why or why not?