Re-point, re-order, re-grant or disable a registered extension
Changes the endpoint, the points, the slots, the position, the grants or the enabled flag. Requires extensions:manage. Widening a grant additionally requires the caller to hold that permission themselves, so nobody hands an extension reach they do not have; narrowing one never does.
Four things here can reach the extension, and a disable outranks all four. A moved endpoint, an explicit refreshModels, a tool-server slot this update claims that the row did not already hold, and — belonging to that slot alone — an enabled going from false to true on a row that already holds it. And then only for what the row has to answer for. A moved endpoint reads BOTH, the model list if the row claims model-supplier and the tool listing if it claims tool-server, because a new address is a new answer to both questions; refreshModels reads the model list alone; a newly-claimed tool-server slot, or an enable of a row already holding it, reads the tool listing alone — an enable being the moment this home starts serving that registration’s tools, and the one road onto that which nothing else here checks. Claiming model-supplier reaches it not at all by itself: the claim alone stores that slot claimed and unserved, and refreshModels is what reads its catalogue, so an extension that verifies signatures is never called under a secret it does not hold yet. Everything else — a narrowed grant, a moved position, a changed point list, another slot, the switch itself — is a write against this home’s own row that completes whether or not anything answers there.
Each of those reads FAILS CLOSED. A model list or a tool listing this host asked for and could not read — a refused connection, a timeout, a non-200, a body that does not parse — answers 422 and changes nothing, rather than storing a claim it could not check. Either way, check the endpoint answers /tools — or /models for a supplier — under the secret it holds, then make the write again.
Carry refreshModels: true in the same body to claim the supplier slot and read its catalogue in one write, so long as the row that update produces remains enabled. The flag outranks every other field the body carries but never the switch, so {"slots": ["model-supplier"], "refreshModels": true} does call the endpoint — the shortest correct road once the far side already holds the secret — while that same body carrying "enabled": false calls nothing at all and stores the claim unserved. Where the read does happen it is all or nothing: if it fails, the whole update answers 422 and nothing is stored, so the slot is never left claimed behind a read that did not happen.
When the row this update produces is switched off, nothing is asked of that address, however the body is written. {"enabled": false, "refreshModels": true} and a disable that also moves the endpoint store the change and call nothing: the moment an owner most needs to disable an extension is the moment it has stopped answering, and a veto its subject can refuse by going quiet is not one. What such a body asked for is stored rather than performed — the new address and the newly claimed slot are on the row — and re-enabling does not read the model list either, because switching a row back on says nothing about what that address now serves. Send refreshModels: true, with the enable or after it, to read the list at the address the row now carries. The tool listing is the exception: an enable of a row holding tool-server reads it, as above.
On a row Lerian installed (installedByOperator), enabled is the only field this may touch, in both directions. Off is the owner’s standing veto and on again is allowed because re-enabling Lerian’s own endpoint discloses nothing that was not disclosed when Lerian installed it. Every other field is refused: an owner who could re-point that endpoint could aim an operator-marked, Lerian-audited registration at an address of their own.
Authorizations
Enforced on every transport, with no exempt operation. A person's request — over the default local unix socket exactly as over a TCP listener — must carry a JWT issued by the configured identity provider, which the host verifies itself against that issuer's key set: signature, issuer, expiry, and the person and organisation it names. Requests without a valid one receive 401 NRY-0011. The socket's file permissions are transport and are not an authorisation.
Path Parameters
The publisher half of the extension's namespaced name.
The name is publisher/extension and travels as TWO path segments rather than one. The single-segment spelling is not broken — a generated client percent-encodes a path parameter's slash and the router decodes it back — so this is a deployment choice rather than a repair: an encoded slash is normalised or rejected by most reverse proxies a hosted home sits behind, and two segments read the way the name reads, /v1/extensions/lerian/redactor.
^[a-z0-9][a-z0-9-]*$"lerian"
The extension half of the namespaced name.
^[a-z0-9][a-z0-9-]*$"redactor"
Body
What a change to a registration may carry. An omitted property is left alone; a change carrying nothing at all is refused rather than answered 200, because a caller who believes they changed something and did not is exactly who that saves.
The owner's switch, and the only property this operation may carry on an operator-installed row.
Switching a tool-server row ON reads its tool listing, and is refused 422 if that listing cannot be read or 409 if a name in it is one this home already serves. The cost is stated rather than hidden: an owner switching a tool server back on is held up while that endpoint is down, and must wait for it or drop the slot.
Switching OFF reaches the endpoint not at all, in either direction of that rule and whatever else the same body carries. The moment an owner most needs to disable an extension is the moment it has stopped answering, so a kill switch that first waited on its own subject would work only while the thing it kills is healthy.
Ask this host to call the extension's list-models operation and replace the models stored on its registration.
On a supplier whose catalogue has never been read it is the FIRST read rather than a re-read, and it is one of the two mechanisms that perform that first read: claiming model-supplier stores the slot unserved, and this flag is the direct way to make it serve anything at all. The other is a moved endpoint in the same update or a later one, which reads the list for its own reason — a new address is a new list — and so serves a claimed-and-unserved slot as a side effect. Either works only while the row the update produces is enabled. Send it once the far side holds the secret the registration response handed over.
It is a flag on a write rather than a background poll, so the catalogue moves when the owner says it moved and nobody has to reason about a model list that changed on its own. The host also refreshes on its own when this update MOVES the endpoint, because a new address is a new list by definition.
Every other change contacts the extension not at all, with two exceptions this flag is not part of: a tool-server slot this update claims that the row did not already hold reads that registration's tool listing, for the same reason a moved endpoint does — a new claim is a list this home has never read — and so does an enabled going from false to true on a row already holding that slot. A disable, a narrowed grant and a moved position are writes against a row in this home and complete whether or not anything answers at that address — which is what makes the owner's disable a kill switch that works at the moment it is most needed, rather than one that works only while the thing it kills is healthy.
Carried alongside enabled: false, this flag does nothing, and neither does a moved endpoint in the same body: the resulting row is off, so this host asks that address nothing. The move is stored; the refresh is not deferred and not queued. Ask again — the flag on the update that switches the row back on, or on a later one — when the extension is live and you want its list read.
Response
The registration as it now stands, without its secret.
An extension this host carries: a program this home REGISTERED at an HTTPS address. Never its secret, on any response that uses this schema.
package is absent on a registered extension, which has no package — it is another deployment rather than anything installed here.
Namespaced name, publisher/extension form.
^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$The installed package that carries this extension. Absent for a registered extension, which has no package.
500Operations invokable via POST /v1/extensions/{name}/invoke.
The HTTPS address this host calls.
The decision points this extension sits on.
The slots it occupies.
Where it sits in the ordered pipeline, lowest first.
The permission slugs granted ON this registration. An extension reaches every unmarked session in its home; a private one only where sessions:read-private is granted here.
What a registered supplier serves. Absent unless the extension occupies a supplier slot — and absent there too while that slot is CLAIMED AND UNSERVED. A supplier with nothing here offers nothing to any turn: the catalogue skips such a row exactly as it skips a disabled one, so a slot nobody has served yet is not selectable and not a provider anybody sees.
Whether this home is serving the tools this registration publishes RIGHT NOW — that is, whether a model in this home can call them. Present on every extension that occupies the tool-server slot and absent on every one that does not, because it is a fact about that claim and about nothing else.
It is false far more often than an owner expects, and that is the reason this field exists. The write that registers a tool server reaches the endpoint not at all, so the slot is stored CLAIMED AND UNSERVED and stays that way until a sweep has read the endpoint's tool listing; and a listing whose names collide with tools this home already serves is refused by the tool registry, which leaves the registration standing and publishing nothing.
It is the last answer this home RECORDED, never a guess made at read time, so it survives a restart: a home brought back up under a standing collision still says it is refusing those tools rather than reporting them unread.
Why this home is not serving that registration's tools, in this host's own words — never a sentence the far side wrote. Present exactly when servingTools is false, and absent when it is true, because a reason for something that is not the case would be the two fields disagreeing.
It can say: that no sweep of this home has read the listing yet (every claim starts here, and a host built without a registered-extension pool stays here); that the tool registry refused the listing because another source already owns one of those names, naming both claimants; that the endpoint could not be reached or would not answer; that the listing carried no tool this host can serve; or that the owner has the registration switched off.
The owner's switch. A disabled registration is still listed, and still says where it pointed, because a registry that hid one would answer "what reads our conversations" with less than the truth.
True when Lerian installed this on a home Lerian hosts, without the owner's act. It travels on every read because it is the notice standing where consent would otherwise be: a member can always tell which extensions are theirs and which are Lerian's.
The subject of the verified token that registered it. Absent on an operator install, where there is no person to name.

