{
  "schema": "leumas.docs.page/1",
  "id": "skill:leumas-studio#domains/studios",
  "slug": "skills/leumas-studio/domains/studios",
  "kind": "tools",
  "bucket": "skill",
  "title": "Studio Network — what you can do",
  "name": "Studio Network",
  "eyebrow": "what you can do",
  "chip": null,
  "summary": "<!-- GENERATED by ops/tools/skill-sync — do not edit by hand. Run pnpm skills:sync. -->",
  "keywords": [
    "leumas-studio",
    "fetch remember",
    "tabs focus",
    "skill-sync",
    "skills",
    "pnpm",
    "edit",
    "hand"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "<!-- GENERATED by ops/tools/skill-sync — do not edit by hand. Run `pnpm skills:sync`. -->\n\n# Studio Network — what you can do\n\n`/admin/d/studios` · pillar `protocols`\n\nThis Studio's address on the Leumas network — find other studios, see where they are, and message them.\n\n## Fetch this, never remember it\n\nThese change per install and per release. Call for them - a list written here would be a future lie.\n\n| What | Call | Why |\n|---|---|---|\n| whether this install is on the network at all, and what it publishes | `GET /api/studio-network/me` | ALWAYS call this first. `connected:false` with a `reason` is the normal state on a dev box or an unactivated install, and it is not an error — every other answer in this domain depends on it |\n| who else is listed, right now | `GET /api/studio-network/directory?q=<term>` | the opt-in directory, read through this install's own licence token. `?handle=<address>` resolves one exact address and 404s when nobody holds it |\n| this install's conversations and unread counts | `GET /api/studio-network/threads` | the local copy — mail is drained off the relay with an advancing cursor, so this, not the mailbox, is where a message lives after delivery |\n\n## How to actually do things here\n\nWritten by hand, because the order and the traps are the part no file can derive.\n\n### Get this Studio onto the network\n\n1. `GET /api/studio-network/me`. If `connected` is false, stop and say so — the install needs an activated licence against a central Leumas server, and nothing below will work until it has one.\n2. `PUT /api/studio-network/me` with `{ handle }` to claim the address. 3–30 characters, lowercase letters, digits and dashes.\n3. Then `PUT` again with `{ displayName, about, city, listed: true }` to publish the rest.\n\n**Trap:** The handle is claimed ONCE across the entire network and answers 409 when it is taken or reserved; a malformed one is a 400. The claim runs first and alone, so a refused address leaves the rest of the profile unwritten — retry with a different handle rather than resending the whole form. `listed` is what makes the Studio visible; an address alone does nothing.\n\n### Find another studio and keep it\n\n1. `GET /api/studio-network/directory?q=<term>` — matches a handle PREFIX or a display-name substring.\n2. `POST /api/studio-network/contacts` with `{ handle }` or `{ instanceId }` to save it.\n\n**Trap:** The search matches only those two fields. It cannot search a city, a hostname or a tenant, and that is deliberate: a directory that searches what it refuses to show is a disclosure channel. Saving is idempotent — the same studio twice refreshes one row. A studio the directory has never confirmed cannot be saved at all, so an unlisted or deactivated one answers 404.\n\n### Send a message to another studio\n\n1. Resolve the address first: `GET /api/studio-network/directory?handle=<address>`.\n2. `POST /api/studio-network/threads/<instanceId>/messages` with `{ body }`.\n3. `GET /api/studio-network/threads/<instanceId>` to read the conversation back.\n\n**Trap:** [critical] Not end-to-end encrypted — Leumas relays and can read every payload, and staff see them in the fleet console. Say so before anybody sends anything sensitive. Delivery arrives on the recipient's presence beat (about a minute), not instantly, and it is at-least-once: the handler dedupes on the message id. Sending needs admin on this install, because it speaks as the whole Studio.\n\n### Come off the map\n\n1. `PUT /api/studio-network/me` with `{ listed: false }` to disappear from the directory entirely.\n2. Or `{ city: \"\" }` to stay findable by address while leaving the globe.\n\n**Trap:** Those are two different things and users conflate them. Un-listing hides the Studio from everyone including somebody who knows its address; clearing the city only removes the dot. Neither deletes messages already delivered — a copy lives on the other studio's machine and this one cannot reach it.\n\n## Tabs\n\nEvery one is a real URL. A tab with its own section below carries a real job.\n\n| Tab | Path | Group | What it is for |\n|---|---|---|---|\n| `map` | - | The other studios | Every listed Studio on a 3D globe, at the city it chose to publish. |\n| `directory` | - | The other studios | Search every listed Studio by address or name, and keep the ones you talk to. |\n| `inbox` | - | This studio | Messages between this Studio and another, one thread per studio. |\n| `settings` | - | This studio | Claim this Studio's @address, name it, give it a city, and decide whether it is listed at all. |\n\n## Tabs in focus\n\n### `map` - Map\n\nEvery listed Studio on a 3D globe, at the city it chose to publish.\n\n**Read with:** `GET /api/studio-network/directory`\n\n**Write with:** `POST /api/studio-network/contacts`\n\n1. Click a dot to see who it is, then save the studio or open a conversation with it.\n2. Colour is liveness: green under five minutes since its last beat, amber within a day, grey beyond.\n\n**Trap:** A studio with no published city is NOT on the globe — it is in the Directory instead. Nothing here is ever placed by guesswork, so a missing dot means \"no city published\", never \"somewhere unknown\". Dots in the same city are fanned apart to stay clickable; the fan is a rendering device, not a position.\n\n### `directory` - Directory\n\nSearch every listed Studio by address or name, and keep the ones you talk to.\n\n**Read with:** `GET /api/studio-network/directory?q=` | `GET /api/studio-network/contacts`\n\n**Write with:** `POST /api/studio-network/contacts` | `DELETE /api/studio-network/contacts/:instanceId`\n\n1. Type an @address or a name. An empty box is the whole directory.\n2. Save a studio to keep its name beside its messages.\n\n**Trap:** A contact is a BOOKMARK, not a friendship — it is one-sided and needs no acceptance, because everyone listed chose to be publicly addressable. Do not describe it as a friend request.\n\n### `inbox` - Inbox\n\nMessages between this Studio and another, one thread per studio.\n\n**Read with:** `GET /api/studio-network/threads` | `GET /api/studio-network/threads/:instanceId`\n\n**Write with:** `POST /api/studio-network/threads/:instanceId/messages` | `POST /api/studio-network/threads/:instanceId/read`\n\n1. Pick a thread — each is its own URL — and write.\n2. Opening a thread marks it read.\n\n**Trap:** This is install-level mail: the sender is the machine, and anyone who administers the Studio can read it. Relayed by Leumas and not end-to-end encrypted.\n\n### `settings` - Address\n\nClaim this Studio's @address, name it, give it a city, and decide whether it is listed at all.\n\n**Read with:** `GET /api/studio-network/me`\n\n**Write with:** `PUT /api/studio-network/me`\n\n1. Claim the address first — it is the only field that can be refused.\n2. A city is optional and resolves to that city’s centre. Listing is off until it is switched on.\n\n**Trap:** Never promise finer placement than a city: the form accepts no coordinates and the platform stores none beyond the city centroid. An unresolvable city is kept as a label with no coordinates, which lists the Studio without plotting it.\n\n## What this domain owns\n\nIts verb is **Open**.\n\nEntity kinds: `studios.contact`\n\n",
  "source": {
    "path": ".claude/skills/leumas-studio/reference/domains/studios.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 7182,
    "hash": "02bdf96644d55f3bb8d0f1cf06d0e2904a04e9f2"
  },
  "urls": {
    "html": "/p/skills/leumas-studio/domains/studios",
    "json": "/docs/skills/leumas-studio/domains/studios.json",
    "md": "/docs/skills/leumas-studio/domains/studios.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  }
}
