@@ -0,0 +1,131 @@
1+# Airlock
2+
3+Airlock is the term to describe the set of APIs for interacting with a running Urbit ship from outside of the Urbit network.
4+
5+## Urlock-py
6+
7+Urlock-py is a python library for using Airlock.
8+
9+It consists of a single class `Urlock`. Methods are below.
10+
11+`__init__(url, code)`
12+
13+ Constructor
14+
15+ `url` - url to the http interface of the running ship
16+
17+ `code` - the `+code` of the ship, should never be published
18+
19+
20+`connect()`
21+
22+ Connect to the running ship
23+
24+
25+`poke(ship, app, mark, j)`
26+
27+ `ship` - ship to send the poke
28+
29+ `app` - gall application to send the poke to
30+
31+ `mark` - mark of data sent to the gall application
32+
33+ `j` - json poke data
34+
35+
36+`ack(eventId)`
37+
38+ Send an acknowledgment of receipt of a message so it's cleared from the ship's queue
39+
40+ `eventId` - id of the event to acknowledge
41+
42+
43+`sse_pipe()`
44+
45+ returns the sseclient object
46+
47+
48+`subscribe(ship, app, path)`
49+
50+ `ship` - ship on which the gall application lives
51+
52+ `app` - gall application to subscribe to
53+
54+ `path` - path to subscribe on
55+
56+
57+Below is a simple script to send a message and subscribe to a chat channel. The specifics require an understanding of the `chat-store` and `chat-hook` interfaces.
58+
59+Even without experience with python this should be pretty readable.
60+
61+```
62+#!/usr/bin/python3
63+
64+import urlock
65+import baseconvert
66+import time
67+import random
68+import dumper
69+
70+zod = urlock.Urlock("http://localhost:8080", "lidlut-tabwed-pillex-ridrup")
71+r = zod.connect()
72+s = zod.subscribe("zod", "chat-store", "/mailbox/~/~zod/mc")
73+
74+pipe = zod.sse_pipe()
75+
76+s = baseconvert.base(random.getrandbits(128), 10, 32, string=True).lower()
77+uid = '0v' + '.'.join(s[i:i+5] for i in range(0, len(s), 5))[::-1]
78+
79+p = zod.poke("zod", "chat-hook", "json", {"message": {"path": "/~/~zod/mc",
80+ "envelope": {"uid": uid,
81+ "number": 1,
82+ "author": "~zod",
83+ "when": int(time.time() * 1000),
84+ "letter": {"text": "hello world!"}}}})
85+
86+
87+for m in pipe.events():
88+ dumper.dump(m)
89+ dumper.dump(zod.ack(int(m.id)))
90+```
91+
92+After the imports, instantiating the Urlock class with the address of the running ship and the `+code` from the ship. This code should not be shared. The one used here is for fakezods.
93+
94+```
95+zod = urlock.Urlock("http://localhost:8080", "lidlut-tabwed-pillex-ridrup")
96+```
97+
98+Next we connect to the ship and set up a subscription to chat-store
99+
100+```
101+r = zod.connect()
102+s = zod.subscribe("zod", "chat-store", "/mailbox/~/~zod/mc")
103+```
104+
105+After this we get the SSE pipe that all responses will arrive on.
106+
107+```
108+pipe = zod.sse_pipe()
109+```
110+
111+Next comes some setup for specific implementation details of `chat-hook` and a `poke` is sent to create a new chat message.
112+
113+```
114+s = baseconvert.base(random.getrandbits(128), 10, 32, string=True).lower()
115+uid = '0v' + '.'.join(s[i:i+5] for i in range(0, len(s), 5))[::-1]
116+
117+p = zod.poke("zod", "chat-hook", "json", {"message": {"path": "/~/~zod/mc",
118+ "envelope": {"uid": uid,
119+ "number": 1,
120+ "author": "~zod",
121+ "when": int(time.time() * 1000),
122+ "letter": {"text": "hello world!"}}}})
123+```
124+
125+Finally, the loop here is used to read messages from the sse pipe and send back an acknowledgment. This is important to clear the queue on the ship.
126+
127+```
128+for m in pipe.events():
129+ dumper.dump(m)
130+ dumper.dump(zod.ack(int(m.id)))
131+```