# Channel Core Objects

[Channel hosting protocol](README.md)

## Channel ID

`channel_id` identifies a user-created public channel hosted by one public relay. The creating client MUST generate a new 16-byte `nonce`, encoded as unpadded base64url and matching `^[A-Za-z0-9_-]{22}$`. The same creator MUST NOT reuse a nonce on the same relay; a cryptographically secure random source is recommended.

Channel ID derivation input:

```json
{
  "$type": "meshline.channel.identity",
  "$context": "neo:860833102:0x...",
  "nonce": "base64url...",
  "creator": "neo:860833102:...",
  "relay_id": "0x..."
}
```

| Field | Type | Required | Source and constraints |
|---|---|---|---|
| `$type` | string | Yes | Fixed as `meshline.channel.identity` |
| `$context` | string | Yes | Locally constructed canonical string of the trusted [network context](../../general.md#network-context) |
| `nonce` | string | Yes | 16-byte value generated by the creator's device |
| `creator` | string | Yes | Calling account creating the channel |
| `relay_id` | string | Yes | Hosting relay ID |

Derive the ID as follows; `channel_identity_input` denotes the channel identity input above:

```text
channel_id_input = UTF8(Canonical JSON(channel_identity_input))
channel_id_digest = SHA-256(channel_id_input)
channel_id = "chan_" + base64url(first_16_bytes(channel_id_digest))
```

`channel_id` MUST match `^chan_[A-Za-z0-9_-]{22}$`. Verifiers MUST recompute it from `nonce`, `creator`, and `relay_id` in `ChannelDescriptor` and the trusted context. Name, description, moderators, and state revision do not participate.

## `ChannelDescriptor`

`ChannelDescriptor` is a signed declaration of channel identity, public profile, and administrative state.

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `$type` | string | Yes | Fixed as `meshline.channel.descriptor` |
| `channel_id` | string | Yes | ID derived under [Channel ID](#channel-id), recomputed from descriptor fields and trusted context on verification |
| `nonce` | string | Yes | 16-byte value used in [Channel ID](#channel-id) derivation; immutable after creation |
| `creator` | string | Yes | Creator's [account ID](../core-objects/accounts-and-devices.md#account-id); MUST match device-session account at creation; immutable afterward |
| `relay_id` | string | Yes | Hosting relay ID; immutable and part of ID derivation |
| `name` | string | Yes | Channel name; MUST include a non-[whitespace character](../../general.md#text-whitespace-characters); at most 256 UTF-8 bytes |
| `description` | string | No | Public description; if nonempty, MUST NOT be solely [whitespace](../../general.md#text-whitespace-characters); at most 4 KiB (4,096 UTF-8 bytes) |
| `moderators` | array&lt;string&gt; | No | At most 10 moderator [account IDs](../core-objects/accounts-and-devices.md#account-id); permissions follow [Channel model](concepts/model-and-timeline.md). Compare complete strings case-sensitively; may be omitted when empty; no duplicates or owner account |
| `revision` | integer | Yes | Nonnegative consecutive revision, starting at 0 and increasing by exactly 1 on each successful update; range and exhaustion follow [Monotonic revisions and counters](../../general.md#safe-integers-and-counter-advancement) |
| `status` | string | Yes | `active` at creation, `closed` after closure; see [Timeline lifecycle](concepts/model-and-timeline.md#channel-timeline-lifecycle) |
| `created_at` | integer | Yes | Unix seconds when the creator's device signed the initial descriptor; unchanged on updates |
| `updated_at` | integer | Yes | Client Unix seconds when signing this revision |
| `device_signature` | string | Yes | Calling device's 64-byte Ed25519 signature over this descriptor, unpadded base64url |

Complete Canonical JSON UTF-8 encoding MUST NOT exceed 8 KiB (8,192 bytes), including `device_signature` and all unknown properties. Individual field constraints and total size both apply.

### Signing and Verification

Exclude root `device_signature` and construct signing input under [Network-bound JSON inputs](../../general.md#network-bound-json-inputs).

Different revisions may be signed by different devices of the creator account. Timeline outer fields and certificate references follow [ChannelEvent](#channelevent) and [Device certificates for channel events](#device-certificates-for-channel-events).

Verifiers MUST validate the signer's certificate signatures and identity binding under [`DeviceCertificate`](../core-objects/accounts-and-devices.md#devicecertificate), confirm certificate `account` exactly equals `creator`, verify `device_signature` with its signing public key, recompute `channel_id`, and confirm `relay_id` is the connected hosting relay. They MUST also obtain its valid `RelayDescriptor` and confirm `channel.host.v1` when using channel hosting.

At creation, update, or closure acceptance, the host MUST confirm current signing-device authorization. An accepted descriptor still required for retention does not become invalid because its device certificate later expires or the device is removed.

### Caching and Historical Descriptors

Clients may save descriptors and signer certificates obtained through `channel.resolve` or `channel.read`. Descriptors from QR codes or third parties are only unverified material; clients MUST connect to the designated relay and query the current descriptor with `channel.resolve`.

To update verified saved current state, a descriptor MUST pass applicable checks and have a strictly greater revision. Equal or smaller revisions MUST NOT overwrite current state. A historical descriptor obtained by specific revision may verify corresponding events but MUST NOT replace an existing descriptor of that revision or roll current state back.

Retention, queryability, and historical event verification follow [Timeline lifecycle](concepts/model-and-timeline.md#channel-timeline-lifecycle).

## `ChannelEvent`

The timeline orders creation, descriptor updates, and public content operations by host acceptance. `ChannelEvent` is returned by `channel.read`:

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `sequence` | integer | Yes | Nonnegative strictly increasing sequence assigned within this channel; creation is 0 and subsequent values exceed the current head; MUST be a [safe integer](../../general.md#safe-integers-and-counter-advancement) |
| `descriptor_rev` | integer | Yes | Nonnegative safe-integer descriptor revision: the embedded descriptor's revision for descriptor events, or effective revision at acceptance for content events |
| `payload` | object | Yes | Complete business object: known types are [`ChannelDescriptor`](#channeldescriptor), [`ChannelPost`](methods/timeline.md#channelpost-1), [`ChannelPostEdit`](methods/timeline.md#channelpostedit-1), and [`ChannelPostDelete`](methods/timeline.md#channelpostdelete-1); future unknown `$type` values are allowed |
| `accepted_at` | integer | Yes | Hosting relay's local Unix seconds at acceptance |
| `signer_device_id` | string | Yes | Business-object signing device ID, referencing this page's `certificates` |

The relay generates `sequence` and `accepted_at`, records `descriptor_rev` as above, and takes `signer_device_id` from the accepting device session. These outer fields are not part of business-object signing input. Append, allocation, and cleanup follow [Timeline lifecycle](concepts/model-and-timeline.md#channel-timeline-lifecycle); writes meet [Atomic commit and persistence rules](concepts/model-and-timeline.md#atomic-commit-and-persistence-rules).

Clients MUST confirm every known business object's `channel_id` matches the query. When updating state from one response, they MUST apply verified events in strictly ascending sequence order.

Sequence 0 MUST contain the initial revision-0 `ChannelDescriptor`; every descriptor event's outer `descriptor_rev` MUST equal its embedded revision. Each subsequent descriptor has a unique positive revision. Cleanup may leave gaps in readable revisions and timelines, but assigned revisions MUST NOT be reused or renumbered.

All known business-object signatures use the certificate referenced by outer `signer_device_id`. Unknown events need not use known-object field structures and cannot change known channel state; clients may consume their sequences as ignored events.

### Device Certificates for Channel Events

Every channel event identifies its business-object signer through outer `signer_device_id`. The host verifies with the calling device's certificate, records its derived ID after acceptance, and retains the certificate needed for historical verification while the event is readable. The certificate's account identifies the creator, poster, editor, or deletion initiator.

`channel.read` supplies certificates referenced by the page's signer IDs in response-level `certificates`, deduplicated by derived device ID.

With multiple certificate versions for one device, the relay may return any validly signed version through `channel.read` or `channel.resolve`. Clients verify historical objects with the device signing public key; current expiry or authorization MUST NOT retroactively invalidate accepted operations.

Clients MUST verify every response certificate under [`DeviceCertificate`](../core-objects/accounts-and-devices.md#devicecertificate), derive distinct device IDs, and confirm their set covers every operation-device reference on the page. Certificate order has no protocol semantics.
