91 — Channels
BuiltRendered from 91-channels.md in the public repository. Edits belong there.
A channel is a read-only session surface that people or instances outside the instance use. It is backed by a sandbox the instance hosts for it. Channels are in micyte/channels/.
| Register | Used by | Writes |
|---|---|---|
Tool (micyte/tools/) | the operator, on their own data | through the datum write path |
Routine (micyte/automation/) | nobody; it runs when its condition holds | yes |
Channel (micyte/channels/) | callers outside the instance | no |
Access
- Open (
access = "open"): any caller. Routes are under/__channel/(outside/portal):GET /__channel/andGET /__channel/<channel_id>/session. Only GET routes are registered. An unknown channel returns 404. - Closed: callers holding an active contract with this instance, through
GET /__channel/<channel_id>/contract-session. Admission is described in 90.
The contract
HostedChannel in micyte/channels/__init__.py is a runtime-checkable Protocol: channel_id, label, summary, access, sandbox, tabs, and build_panel_payload. The operator's preview of a channel and the public session are built by the same build_panel_payload call, so they return the same payload.
register() raises TypeError for an object that:
- does not satisfy the Protocol;
- has a
tool_id,routine_idorroute; - declares
writes.
It raises ValueError for an access other than open or closed, and for a channel_type that is not one of the four types.
A channel has no write path because an anonymous request to it runs with the operator's identity.
Because register() checks the Protocol with isinstance, adding a member to the Protocol makes every channel without it fail registration. New optional attributes are read with getattr and a default instead. The same applies to WorkbenchTool.
Joining an open channel: alias_fields
alias_fields is one of those optional attributes. It lists the values a member supplies to take a place in an open channel, each as {"name", "label", "required", "max_length", "hint"}. A channel without it cannot be joined from a profile.
The channel still writes nothing. The host writes two records:
- a row in the channel sandbox's
accountsroster (archetypechannel_account), holding the member's msn, the suppliedtitle, the roster state the channel's local domain labelsengaged, and the day; - an empty alias document in the member's own
systemsandbox, named<channel>-<host msn>, which records which host's channel the member belongs to.
agnet declares one field, title: the name other members see, in plain ASCII, at most 64 characters. convention declares none.
The channels in this repository
agnet
micyte/channels/agnet.py. An open market channel. Tabs: supply, demand, taxonomy, profiles.
- Supply and demand list products and prices by day from the market records published to the channel's sandbox.
- Taxonomy is built only when that tab is requested; other requests return
{"loaded": false}. A taxon is markedtradedwhen a product in the catalogue resolves to it, and the tree opens along the ancestors of traded taxa. - Profiles draws every profile on a map, coloured by its group, and as a list beside it. A search, a type, live or still, and the group chips narrow the map and the list together, in the browser, from one payload; nothing is fetched. Each county is named at the middle of its own profiles. No network probe is made.
The channel holds profiles for producers, hubs and consumers, typed by the registrar's ag_profile branch (46d). They are grouped producer, hub, consumer, then the rest of the branch in its own order. A profile is live when the subject's account with the channel is whole: a place on the channel's roster (accounts) and the alias document the subject's own instance keeps (hosted alias interfaces), which is the interface that instance initiated. A place with no alias is a write that stopped between the two halves, and that profile stays still. Every other profile is still.
Every profile is ONE row of ONE document, the channel sandbox's agnet_profiles, in the sandbox's own numbering, holding only what the channel adds to the registrar's record. Every cell is present and empty when unknown, so a fact learned later is an edit in place and never a move: the row's arity is its address.
| Field | Cell |
|---|---|
msn_id | 3-1-4. The subject, which keys everything the registrar holds about it: title, email, phone, website, domain |
entity_kind | 3-1-13. The ag type, a node of the registrar's ag_profile branch |
coordinate | 3-1-3. Where it is, as a HOPS coordinate |
title | 3-1-2. The profile's OWN name, only when the registrar holds no such name for the subject (a market a city runs is not the city). Empty otherwise |
owner_msn | 3-1-47. A principal owner, one cell for each. Its own reserved field, so that an owner's msn never folds into the subject's |
Nothing the registrar holds is repeated in a profile: a name is "the registrar's" when it is the same name spelled otherwise (Ashtabula County and county_of_ashtabula; Snake Hill Farm and snake_hill_farm_lp). A profile names no archetype, because the archetype library reads 3-1-13 as lcl_id and agnet's numbering reads it as entity_kind.
One module reads the document and builds a row's head: micyte/domains/registry/agnet_profiles.py. The channel's profiles tab, the registrar directory (Director's detail shows an entity's AGNET type), the network map and the plot manager all read through it, and a writer writes through it, so no reader keeps markers of its own. Until 2026-11-12 a second document, member_ag_profiles, held most profiles in the registrar's numbering and repeated the registrar's title and domain. It was folded into agnet_profiles and deleted. A sources manifest declares the document as agnet_agnet_profiles, the channel's own included: a bare agnet_profiles reads as the agnet sandbox's profiles.
agnet names agnet_app as the application that governs it (see The Channel tab). A host that registers an app under that id draws the channel as that app's Channel tab, and can draw the same profiles section as a tab of its own with build_profiles_session.
If the sandbox's channel_profile records a different tab list from the code, the session reports the drift.
convention
micyte/channels/convention.py. An open channel with one tab, convention. It returns the archetype library tree's current id and hash and the convention it states, so another instance can compare its own pin.
A host can register its own channels through the same register(); those are not part of this repository's channels.
Channel types
Every channel app declares one of four types in channel_type. The types are data in micyte/channels/types.py: each one lists its member roles and the kinds of post its members make. A channel that is not a channel app, such as convention, declares "". The open catalogue (GET /__channel/) publishes each channel's type.
| Type | channel_type | Members | Posts |
|---|---|---|---|
| Assembly | assembly | one role, member. An office or kind is something a member holds, not a separate role. Usually closed; each member sees their own standing | none declared |
| Broadcast | broadcast | broadcaster, who posts to a feed, and follower, who follows or views it | feed_post, by a broadcaster |
| Community Network | community_network | one principal profile, member. Producer, hub and consumer tell members apart | sell_order, buy_order, transaction |
| Subject Network | subject_network | alias profiles, which form groups inside a public channel | none declared. Provisional: whether this type is kept is not decided |
Community Network. A member's profile type is not an interface variation: interface_by_role is false, so a producer, a hub and a consumer see the same interface, and the type is data on the profile. A producer's shipment available to buy is a sell order; a consumer's order is a buy order. Each becomes a transaction post on completion, and the transaction is the only post the channel keeps. A hub reports the orders it fills as transactions, as though they happened with itself, which is how it contributes to pricing and demand.
agnet is a Community Network. A host's own channels declare their type the same way.
The Channel tab
A channel names the application that governs it in governed_by, as (tool_id, variant). An empty variant means every configuration of that app. A declared app draws a Channel tab, before its Domain tab, for every registered channel that names it. The channels are found when the app is drawn, not when it is imported, because a host registers its own channels after micyte loads. No app declares the tab, so none can leave it out.
The tab (micyte/channels/preview.py) shows:
- the channel, its type and its access class;
- a choice of channel, when the app governs more than one (
?channel_preview=); - for a channel with a per-member reading (
wants_viewer_identity), a choice of the member to preview as, from the channel'spreview_viewers(authority_db_file)(?channel_viewer=); - a window holding the session the channel's own
build_panel_payloadreturns for that member, drawn by that session's own renderer.
The window is the member's interface, not a copy of it, and it cannot act. Every key ending in _door is removed from the session at any depth, and the session carries preview: true. A channel with no member to preview as says so instead of drawing an empty window.
Still a design
A participant holds an alias profile, a datum document with its own archetype held by both ends. A member's interface is fed in one of three ways per surface: a compiled view; rows filtered by the member's lcl_id; or whole documents whose title carries the member's lcl_id. No channel records posts of the declared post types yet.