a bird-brained guide to:

noun channels

alright, there is a new option for browser frontends to talk to their respective urbit gall apps. now we can send nouns across eyre. these nouns can contain the classic urbity things we know and love, like %poke, %subscribe, %ack, etc.

the past

if you need a little background, typically browser based urbit frontends would only "speak" json. so when a user clicked a button that needed to either %poke the urbit backend to perform an action or to %subscribe for continuous updates, the frontend stitched together json objects that would make it's way to the urbit agent, where it would be interpreted and converted into the %poke or %subscription.

this was always an annoying point where the legacy internet had seeped into the urbit world, and it went as far as requiring urbit to understand json, because there wasn't a good way of transfering urbit shaped info (aka nouns) across classic internet space (aka http).

pipes

maybe that reveals some of the motivation of why this has been made, but I should also mention the medium from which a classic browser frontend "speaks" to an urbit backend. this medium is eyre-- the http handling vane.

eyre will open url paths into your urbit. it will also verify that only knowers of the +code has access to your urbit. when you pull up your urbit login page and enter your code, this happens through eyre.

specifically, this happens through eyre channels. when connection is made and authenticated, a channel is defined. the channel is where info passes between your ubit and a browser. by default all that communication is encoded in json. but now that changes with the introduction of noun channels!

hurray!

an exercise for my own sake

in order to understand how noun channels will be used in practice, I want to start by trying to tweak a simple exercise from the eyre reference guide.

under the Basic section is a walkthrough of Authenticating and Using Channels. this is a great place to start. we should only need to alter a few lines of code to get the new noun channels to give us something fun.

I'll just copy the exercise here and try to highlight our changes:


“Here we'll try authenticating with the default fakezod code.”

“Using curl in the unix terminal, we'll make an HTTP POST request with "password=CODE" in the body:”

curl -i localhost:80/~/login -X POST -d "password=lidlut-tabwed-pillex-ridrup"

“Eyre will respond with HTTP status 204, and a set-cookie header containing the session cookie.”

HTTP/1.1 204 ok
Date: Mon, 17 Jul 2023 23:55:39 GMT
Connection: keep-alive
Server: urbit/vere-2.11
set-cookie: urbauth-~zod=0v7.ugkao.49atj.pkfjn.n8fg3.bde58; Path=/; Max-Age=604800

“The urbauth-.... cookie can now be included in subsequent requests (e.g. to the channel system) by providing it in a Cookie HTTP header.”


this should be the same for our noun channels.

let's continue into the Using Channels section.


“You will be copying the entry of the set-cookie field into the --cookie field in subsequent commands.”

“Now that we have our cookie, we can try poking an app & simultaneously opening a new channel. In this case, we'll poke the hood app with a mark of helm-hi to print >

"Opening airlock" in the dojo.”

“We'll do this with an HTTP PUT request, and we'll include the cookie we obtained when we authenticated in the Cookie header. The URL path we'll make the request to will be http://localhost:80/~/channel/mychannel. The last part of the path is the channel UID - the name for our new channel. Normally you'd use the current unix time plus a hash to ensure uniqueness, but in this case we'll just use mychannel for simplicity.”

“The data will be a JSON array containing a poke action:”


here's where we diverge and go with the changes mentioned in the eyre noun channel guide.

first, the header will now be --header "content-type: application/x-urb-jam". and second, the data will be an urbit %poke to %hood. but we'll need to go through a few steps first.

ultimately the noun will be in the form of @uw which is a base64 integer. this is super helpful for writing short snippets for the insanely large numbers that nouns can be. in this case, we'll be PUTing the following noun.

0wb.AOIz2.OJx0V.eHuYA.3uW43.aSA3a.RIbEL.w3iQ5.HqScH.hu0sz.uTJ7M.6ssHm.TK7M5

where the heck did that come from?!

it's a one and half-step process. let's start with the %poke itself. [%poke request-id=1 ~zod %hood %helm-hi 'take me to your leader']

this %poke is in the form required for a $channel-request as defined in eyre.hoon. look at the eyre noun channel guide for all those.

the noun channel expects a list of $channel-requests in the body or --data section of the http request. but on top of that, it requires that list to be jammed, like using +jam. and then of course in the aura of a @uw.

if you were to do this in a dojo, or wherever in urbit land, the process would look like this:

`@uw`(jam [[%poke request-id=1 ~zod %hood %helm-hi 'take me to your leader'] ~])

let's put it together!

using our urbauth cookie, replacing the header for x-urb-jam, and sending our base64 jammed poke to %hood gives us this baby:

curl --header "content-type: application/x-urb-jam" \
     --cookie "urbauth-~zod=0v7.ugkao.49atj.pkfjn.n8fg3.bde58" \
     --request PUT \
     --data '0wb.AOIz2.OJx0V.eHuYA.3uW43.aSA3a.RIbEL.w3iQ5.HqScH.hu0sz.uTJ7M.6ssHm.TK7M5' \
     http://localhost:80/~/channel/my-channel

we just sent an http PUT request to our urbit. the %poke was asking dojo to print a message. go check it out!

you should see:

< ~zod: take me to your leader

hot diggidy dang

we did it!

the eyre guide continues and so does the noun channels guide. so let's see if we can GET updates from a subscription using noun channels.


“Now we can connect to the mychannel channel we opened. We do this with an HTTP GET request with our session cookie and the same path as the last request:”

curl -i --cookie "urbauth-~zod=0v7.ugkao.49atj.pkfjn.n8fg3.bde58" -H "x-channel-format: application/x-urb-jam" http://localhost:80/~/channel/my-channel

notice that we needed to add the header x-channel-format: application/x-urb-jam for this GET request.


“Eyre will respond with an HTTP status code of 200 and a content-type of text/event-stream, indicating an SSE (Server Sent Event) stream. It will also send us any pending events on the channel - in this case the poke ack as a poke response for our original poke:”

HTTP/1.1 200 ok
Date: Tue, 18 Jul 2023 00:57:38 GMT
Connection: keep-alive
Server: urbit/vere-2.11
set-cookie: urbauth-~zod=0v7.ugkao.49atj.pkfjn.n8fg3.bde58; Path=/; Max-Age=604800
connection: keep-alive
cache-control: no-cache
content-type: text/event-stream
transfer-encoding: chunked

id: 0
data: 0w2RIr2.mIHmT.K7U1N

notice how the data is base64.

in the json version of this you'd receive:

 data: {"ok":"ok","id":1,"response":"poke"}

if you are getting errors like 400 or 406 look at the eyre noun channel guide for what might be going wrong.

it's probably the headers or your jammed noun. but it could be the channel is already in json mode and you'll need to delete it first.

what's inside that data?

let's look at this data from within the dojo. the noun channel guide says we'll receive a @uw-encoded jam of the structure [request-id=@ud channel-event].

> ;;([request-id=@ud channel-event:eyre] (cue 0w2RIr2.mIHmT.K7U1N))
[request-id=1 [%poke-ack p=~]]

it appears we've received a %poke-ack. congratulations everybody!

note: cue is the reverse of jam.

tapping out

the eyre guide continues by making a PUT request for %subscribeing to %graph-store /updates using curl. I believe that excercise should be doable from this point. so let me instead write down a few thoughts about what I learned.

to recap for myself, we used curl to simulate a frontend connecting to an urbit using the eyre vane. with the new noun channels we can send data our urbit natively understands within the body of the http.

here's a thought. this requires the frontend to be capable of jamming and encoding numbers in base64. jamming being what I'd expect to be the harder of the two. and thinking of the list with our %poke in it, the frontend probably needs a sense of what a cell is and maybe urbit's notion of strings. I'm not totally confident in all that. nockjs allows js to know things like cells and nouns, and handles big-integers like base64, but I'm not sure if it does jamming.

somebody dm me if you know how to jam a noun in js.
--request from ~nordus-mocwyl summer 2023


hey, I got a dm response!

“yeah nockjs supports this” ~palfun-foslup

and there it is in the serial.js file. which makes sense because we jam the serialized request-ids plus the list of channel-requests into one atom.


your feathered friend,
nord bird

references

[1]: the POST section is relevant, but most everything else is describing the json mess from a hopefully soon forgotten age.