@@ -11,65 +11,50 @@ work.
1111 (yours) (private)
1212```
1313
14- ** subs always initiates.** The registry never calls subs, never needs to reach
15- it, and never needs a public address for it. Your registry is an HTTP server
16- that subs polls.
14+ ** subs always initiates.** The registry never calls subs and never needs a
15+ public address for it. Your registry is an HTTP server that subs polls.
1716
1817## Scope
1918
20- This document covers ** only** the four endpoints subs calls. Everything else
21- about your registry is yours to decide and subs neither sees nor cares about:
19+ This document covers ** only** the four endpoints subs calls. Everything else is
20+ yours to decide and subs neither sees nor cares about: how requests get in, how
21+ you authenticate or bill the people making them, how you store or display them.
2222
23- - how handle requests get in — a public form, a paid checkout, an admin panel
24- - how you authenticate, bill, or rate-limit the people making requests
25- - how you store registrations, notify users, or expose status to them
26-
27- Implement the four endpoints below and subs will work with it.
2823[ ` examples/registry-server ` ] ( examples/registry-server ) is a working
29- implementation you can run and read; the intake it happens to ship is
30- illustrative, not part of this contract.
24+ implementation you can run and read.
3125
3226---
3327
3428## The cycle
3529
36- Each pass, subs:
30+ Each pass, per space, subs:
3731
38321 . ` GET /pending ` — collect handles awaiting registration
39- 2 . stages them locally ( validating and de-duplicating)
40- 3 . ` POST /ack ` — report which ones it took
33+ 2 . stages them locally, validating and de-duplicating
34+ 3 . ` POST /ack ` — report what it took
41354 . publishes certificates to the relay network
4236
43- Steps 1–3 are what your registry participates in. Step 4 is internal to subs.
44-
45- Later, when a batch is committed on-chain, subs can call
46- [ ` POST /committed ` ] ( #post-committed ) — that one is ** not** automatic.
37+ Steps 1–3 involve your registry; step 4 is internal to subs. ` POST /committed `
38+ fires separately and is ** not** automatic.
4739
4840---
4941
5042## Authentication
5143
52- All four endpoints are authenticated with a single shared bearer token:
44+ All four endpoints take a single shared bearer token:
5345
5446```
5547Authorization: Bearer <token>
5648```
5749
5850Configure it in subs under ** Settings → Registry Server → Auth Token** . Reject
59- anything without a valid token with ** ` 401 ` ** . subs surfaces ` 401 ` and ` 403 `
60- distinctly from other failures, so an operator sees "registry rejected the auth
61- token" rather than a generic upstream error.
62-
63- This token grants access to your work queue — reading pending handles and
64- marking them staged or committed. If other systems of yours write to the
65- registry, give them their own credentials; there's no reason for them to share
66- the one subs holds.
51+ anything without a valid token with ** ` 401 ` ** ; subs surfaces ` 401 ` /` 403 `
52+ distinctly, so an operator sees "registry rejected the auth token" rather than a
53+ generic upstream error.
6754
68- ** ` /health ` is authenticated too** , deliberately. subs' ** Test** button probes
69- it, so a successful test proves both reachability * and* that the token is
70- accepted — rather than showing green for a registry that will reject every
71- request after it. If you need an open liveness probe for a load balancer,
72- expose it on a path subs doesn't use.
55+ ** ` /health ` is authenticated too.** subs' ** Test** button probes it, so a
56+ successful test proves both reachability and that the token works. If you need
57+ an open liveness probe, expose it on a path subs doesn't use.
7358
7459---
7560
@@ -81,7 +66,24 @@ Return `200` with any body.
8166
8267### ` GET /pending `
8368
84- Return handles waiting to be staged.
69+ Return handles waiting to be staged. subs asks about ** one space per request** :
70+
71+ ```
72+ GET /pending?space=@example
73+ GET /pending?space=%233438-1-0
74+ ```
75+
76+ ** Return only handles in that space.** A handle for a space this operator cannot
77+ act on can never be staged, so it is never acked — serve it and it comes back
78+ every cycle, forever.
79+
80+ The value is a canonical space label, percent-encoded; numeric spaces look like
81+ ` #3438-1-0 ` , so ` # ` arrives as ` %23 ` .
82+
83+ Scope includes spaces ** delegated to the operator's wallet but not yet
84+ started** — staging the first handle adopts such a space automatically. Scope is
85+ recomputed every cycle, so a new delegation takes effect without restarting
86+ subs.
8587
8688``` json
8789{
@@ -99,31 +101,59 @@ Return handles waiting to be staged.
99101
100102Return ` {"handles": []} ` when there's nothing pending — not ` 404 ` .
101103
102- ** Any non-2xx aborts the entire sync** , including the ack step , so nothing is
103- staged that pass. subs retries on the next cycle. The request times out after
104- 10 seconds.
104+ ** Any non-2xx aborts that space's sync** , including its ack, so nothing is
105+ staged for it that pass. subs retries next cycle. The request times out after 10
106+ seconds.
105107
106108Returning already-staged handles is harmless — subs de-duplicates — but
107109filtering them keeps payloads small.
108110
111+ ** Pagination** is optional: return the full set for the space, or cap it and let
112+ the next cycle collect the rest. subs stages an entire response in one pass.
113+ Capping per space starves nothing, since each request covers one space.
114+
109115### ` POST /ack `
110116
111- Called after subs stages the handles it pulled .
117+ Called once subs has decided each handle's fate .
112118
113119``` json
114- { "handles" : [" alice@example" , " bob@example" ] }
120+ {
121+ "handles" : [
122+ { "handle" : " alice@example" , "outcome" : " staged" },
123+ { "handle" : " bob@example" , "outcome" : " already_committed_different_spk" }
124+ ]
125+ }
115126```
116127
117128Move these out of your pending set. Return ` 2xx ` ; the body is ignored.
118129
119- ** This must be idempotent.** subs acks every handle it pulled, including ones
120- already staged locally, and re-acks after a failure. Re-acking an
121- already-acked handle must succeed, not error.
130+ ** Every outcome is terminal** — a handle appears here only once settled, and
131+ will not be offered again.
132+
133+ | Outcome | Meaning | What to tell the user |
134+ | ---| ---| ---|
135+ | ` staged ` | Accepted; awaiting commitment | In progress |
136+ | ` already_staged_same_spk ` | Already pending under the same owner | In progress — a duplicate request |
137+ | ` already_committed_same_spk ` | Already registered to this owner | Already theirs |
138+ | ` already_staged_different_spk ` | Another owner has it pending | ** Cannot be fulfilled** |
139+ | ` already_committed_different_spk ` | Another owner already holds it | ** Cannot be fulfilled** |
140+ | ` invalid ` | Not a parseable ` name@space ` handle | ** Cannot be fulfilled** |
141+
142+ The last three can never succeed, however many times they are retried. If you
143+ take payment, they are your refund signal.
144+
145+ ** This must be idempotent.** If the ack fails, subs logs it and continues —
146+ staging already happened on its side — so the next cycle re-pulls, re-stages (a
147+ no-op) and re-acks. The flow self-heals, but only if re-acking succeeds rather
148+ than errors.
122149
123- If the ack fails, subs logs it and continues — staging already succeeded on its
124- side. Your registry still has those handles pending, so the next cycle
125- re-pulls, re-stages (a no-op), and re-acks. ** The flow self-heals, but only if
126- ` /ack ` is idempotent.**
150+ #### Retryable failures
151+
152+ Some handles are neither staged nor settled: subs could not reach a decision,
153+ typically because the space could not be loaded or the wallet could not operate
154+ it. These are ** deliberately absent** from the ack body. They stay in
155+ ` /pending ` , and a later cycle picks them up once the operator's configuration is
156+ fixed. Acking them would mark them done and lose them.
127157
128158### ` POST /committed `
129159
@@ -134,47 +164,30 @@ re-pulls, re-stages (a no-op), and re-acks. **The flow self-heals, but only if
134164Mark these committed and record the root. Return ` 2xx ` .
135165
136166** This is not automatic.** It fires only when someone calls
137- ` POST /registry/notify ` on subs with a ` space ` and ` root ` ; nothing triggers it
138- on a timer. If you need committed state and aren't calling ` /registry/notify ` ,
139- track it by watching the chain or by querying the relay network for the
140- handle's certificate.
167+ ` POST /registry/notify ` on subs with a ` space ` and ` root ` . If you need committed
168+ state and aren't calling that, track it by watching the chain or by querying the
169+ relay network for the handle's certificate.
141170
142171---
143172
144173## Delivery semantics
145174
146175** At-least-once, never exactly-once.** A handle may be delivered more than once
147- — an ack that fails after subs staged the handle is the ordinary case. Design
148- so a repeat delivery is a no-op.
176+ — an ack that fails after subs staged the handle is the ordinary case. Design so
177+ a repeat delivery is a no-op.
149178
150- ** subs is the source of truth for what is registered** , not your registry.
151- ` / ack` means subs accepted a handle into staging; it does not mean the handle
152- is committed on-chain. Only the commit notification means that.
179+ ** subs is the source of truth for what is registered** , not your registry. An
180+ ack of ` staged ` means subs accepted the handle into staging; it does not mean
181+ the handle is committed on-chain. Only the commit notification means that.
153182
154183---
155184
156185## Handle validation
157186
158- ` handle ` must parse as a spaces name in ` name@space ` form. Handles that don't
159- parse are ** skipped and never acked** — so they stay pending on your side and
160- are re-pulled on every cycle, forever.
161-
162- Validate at intake. A malformed handle accepted into your pending queue becomes
163- a permanent poison entry.
164-
165- subs may also decline to stage a well-formed handle:
166-
167- | Reason | Meaning |
168- | ---| ---|
169- | ` already staged ` | Same handle and script pubkey already pending locally |
170- | ` already committed ` | Already committed on-chain |
171- | ` already staged with different spk ` | Conflicts with a pending entry under a different owner |
172- | ` already committed with different spk ` | The handle is taken |
173-
174- These are logged by subs and ** still acked** — they're settled outcomes, not
175- retryable failures. The last two mean the request cannot be fulfilled; your
176- registry has no way to learn this today, so surfacing it to users requires
177- polling on-chain state yourself.
187+ ` handle ` must parse as a spaces name in ` name@space ` form. One that doesn't is
188+ acked ` invalid ` and settled immediately, so it won't linger in your queue — but
189+ validate at intake anyway. A malformed handle that reaches ` /pending ` has
190+ already cost a round trip and, if you charged for it, a refund.
178191
179192---
180193
@@ -191,9 +204,10 @@ In subs' **Settings → Registry Server**:
191204With automatic sync ** off** , the cycle runs only on ** Sync Now** (or
192205` POST /registry/sync ` ).
193206
194- With it ** on** , subs runs continuously: every 30 seconds when idle, every 5
195- seconds while certificates remain to publish. It defaults to off because
196- publishing broadcasts to the relay network.
207+ With it ** on** , subs sleeps 2 seconds * after* each cycle finishes rather than
208+ running on a fixed schedule, so cycles never overlap and a slow one simply
209+ delays the next. It defaults to off because publishing broadcasts to the relay
210+ network.
197211
198212---
199213
@@ -202,7 +216,11 @@ publishing broadcasts to the relay network.
202216- [ ] ` /health ` , ` /pending ` , ` /ack ` , ` /committed ` all require the bearer token
203217- [ ] Missing or wrong token returns ` 401 `
204218- [ ] ` GET /pending ` returns the documented shape, ` {"handles": []} ` when empty
205- - [ ] ` POST /ack ` is idempotent and returns ` 2xx ` for already-acked handles
219+ - [ ] ` GET /pending ` filters on ` ?space= `
220+ - [ ] ` GET /pending ` includes spaces delegated to the operator but not yet started
221+ - [ ] ` POST /ack ` reads a per-handle ` outcome ` and is idempotent
222+ - [ ] The ` *_different_spk ` and ` invalid ` outcomes are surfaced to the user, and refunded if paid
223+ - [ ] Handles absent from an ack stay pending — they are retryable, not settled
206224- [ ] Handles are validated as ` name@space ` ** at intake**
207225- [ ] Repeat delivery of the same handle is a no-op
208- - [ ] ` POST /committed ` implemented, if you need committed state
226+ - [ ] ` POST /committed ` implemented, if you need committed state
0 commit comments