@ours.network/cowork 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # Prerequisites
2
2
 
3
- ours-cowork requires Node.js 20 or newer and one already-running shared ours daemon. Install the operator lifecycle with `npm install --global @ours.network/cli@2.0.1`, then use `ours daemon start` or `ours daemon install-service --yes`. The shared daemon owns broker connectivity and global identity state; cowork attaches through `@ours.network/sdk` 3.0.1 and never starts a runtime of its own.
3
+ ours-cowork requires Node.js 20 or newer and one already-running shared ours daemon. Install the operator lifecycle with `npm install --global @ours.network/cli@2.5.0`, then use `ours daemon start` or `ours daemon install-service --yes`. The shared daemon owns broker connectivity and global identity state; cowork attaches through `@ours.network/sdk` 3.5.0 and never starts a runtime of its own.
4
4
 
5
5
  The selected SDK and CLI must include the external-history storage epoch: structured `listIncomingMessages`/`getHistoryItem`/`getMessages` and `listIncomingFiles`/`fetchFile`/selected `getFiles` APIs backed by per-identity history and immutable blobs. A package with the same nominal version but the older packet-inbox contract is not compatible.
6
6
 
@@ -10,7 +10,7 @@ ours-cowork docs
10
10
  Install and start the shared ours daemon first:
11
11
 
12
12
  ```sh
13
- npm install --global @ours.network/cli@2.0.1
13
+ npm install --global @ours.network/cli@2.5.0
14
14
  ours daemon start
15
15
  ours daemon status --json
16
16
  ```
@@ -6,14 +6,11 @@ The default cowork config file is `~/.ours-cowork/config.json`. It contains only
6
6
  {
7
7
  "version": 1,
8
8
  "stateDir": "/absolute/private/path",
9
- "roomIdentity": { "nameMode": "stable_id" },
10
9
  "rest": { "enabled": true, "port": 3052 }
11
10
  }
12
11
  ```
13
12
 
14
- `roomIdentity` is optional for compatibility with existing version-1 files. Its strict `nameMode` is `stable_id` (the default) or `friendly`. There is no environment or per-room override: an explicit file value wins over the built-in `stable_id` default. The mode is read only when a new room is created; changing it does not rename or reprovision an existing room.
15
-
16
- `stable_id` creates `ours-cowork-<full-room-id>`. `friendly` creates `ours-cowork-<slug>-<full-room-id>`. The slug is derived from the initial normalized room name by NFKD decomposition, removal of combining marks, ASCII lowercase conversion, replacement of each non-`[a-z0-9]` run with one hyphen, edge-hyphen trimming, and truncation to 25 characters followed by trailing-hyphen trimming. An empty result (including an entirely non-Latin or emoji name) becomes `room`. The full 26-character room ID keeps duplicate or collapsed slugs unique; the result satisfies the shared-daemon identity-name character set and 64-character maximum.
13
+ Room identity naming is not configurable. Creation uses exactly `ours-cowork:<normalized room name>`; ID-based and slug-based modes are unsupported.
17
14
 
18
15
  Cowork overrides are `OURS_COWORK_CONFIG`, `OURS_COWORK_STATE_DIR`, and `OURS_COWORK_REST_PORT`. The default console URL is `http://127.0.0.1:3052/`. Setting the REST port enables the loopback listener on that port. Explicitly setting `rest.enabled` to `false` disables both the web console and HTTP room RPC. Configuration, private files, and the complete cowork state directory must remain owned by the daemon account with the modes enforced at startup.
19
16
 
@@ -8,9 +8,9 @@ ours-cowork room list
8
8
  ours-cowork room show <room-id>
9
9
  ```
10
10
 
11
- In the web console, choose Create room, enter Name, Goal, and Briefing, and submit once. Names are trimmed and Unicode NFC-normalized, must contain 1–64 Unicode characters, and cannot contain Unicode control or format characters. Duplicate names are allowed. The created room is selected automatically and its Invite panel opens. Add invitation requirements one at a time; the UI does not combine room creation and invites into a fabricated atomic operation.
11
+ In the web console, choose Create room, enter Name, Goal, and Briefing, and submit once. Names are trimmed and Unicode NFC-normalized, must contain 1–64 Unicode characters, and cannot contain Unicode control or format characters. The normalized name must not collide with an identity already in the shared daemon. The created room is selected automatically and its Invite panel opens. Add invitation requirements one at a time; the UI does not combine room creation and invites into a fabricated atomic operation.
12
12
 
13
- Update the display name with `ours-cowork room settings <room-id> --name "New name"`; other mutable mission fields use the same `room settings` command. A new room uses `ours-cowork-<room_id>` by default, or the configured friendly form `ours-cowork-<bounded-ascii-slug>-<room_id>`. The exact identity name is persisted and frozen at creation. Renaming a room changes only mutable `room_name`; it does not change the identity name, CID, contacts, invites, URLs, history, or earlier author labels. Changing naming configuration likewise affects only rooms created afterward. Duplicate display names and slugs are allowed because every generated identity includes the full room ID, while identity CIDs and room IDs—not names—are authorization keys. Pre-1.0 custom room identities are refused with instructions to back up, recreate, and re-invite rather than being silently renamed or reprovisioned. Inspect admitted seats with `room participants`. A room activates only when its recorded invite requirement is satisfied. Roles are display labels; participant identity CIDs, not roles, are authorization keys.
13
+ Update the display name with `ours-cowork room settings <room-id> --name "New name"`; other mutable mission fields use the same `room settings` command. A new room identity is exactly `ours-cowork:<normalized creation name>`, and that exact authenticated name is persisted and frozen. Messenger therefore displays the room name from the identity itself rather than separate `room_name` metadata. Renaming a room changes only mutable local metadata; it does not change the identity name, CID, contacts, invites, URLs, history, or earlier author labels. Because identity names are daemon-global, attempting to create another room with the same normalized creation name fails closed. Earlier unreleased ID- and slug-based formats are unsupported and receive no migration. Inspect admitted seats with `room participants`. A room activates only when its recorded invite requirement is satisfied. Roles are display labels; participant identity CIDs, not roles, are authorization keys.
14
14
 
15
15
  Close with `ours-cowork room close <room-id>`. Close is forward-only and removes the live standard SDK room identity while retaining the local archive. Archive deletion is a separate explicit operation described in the limitations topic.
16
16
 
@@ -10,7 +10,19 @@ ours-cowork room revoke <room-id> <invite-id>
10
10
 
11
11
  The invite blob is returned only in an invite or recovery receipt. Store or transmit that receipt as needed; durable room metadata intentionally does not store the blob.
12
12
 
13
- The standard SDK does not expose the deleted actor's per-contact invite-origin metadata. Cowork therefore permits at most one live invitation per room. Revoke a public invitation, or let a one-time invitation be consumed, before minting another; every newly accepted SDK contact can then be assigned to one unambiguous room role and requirement.
13
+ Current SDK rooms expose the core's authenticated inviter-side mapping from an
14
+ established contact CID to the existing invite ID used by that handshake. Cowork
15
+ may therefore keep multiple invitations live concurrently: each established
16
+ contact is admitted only through that exact live invite ID and inherits that
17
+ invite's role and requirement. Missing, unknown, revoked, or
18
+ unconfirmed-recovery provenance never admits a seat. This is local read-only
19
+ metadata: it does not change the invite wire format and the redeemer does not
20
+ echo an invite ID back through `addContact`.
21
+
22
+ For compatibility, an older SDK that omits the `origins` field retains the
23
+ single-live restriction. Legacy contacts without a stored origin remain unseated
24
+ after upgrade; Cowork does not guess their role. Upgrade the ours SDK before
25
+ relying on concurrent invitations.
14
26
 
15
27
  The web console shows each returned invite secret once in a blocking receipt. Copy and save it before choosing Done: closing the receipt discards the browser copy. The secret is not placed in the URL, browser storage, logs, or room metadata. One-time and public receipts use the same handling.
16
28
 
@@ -41,6 +41,13 @@ Daemon history responses are capped at 3 MiB of JSON so one 2 MiB file record (a
41
41
 
42
42
  Participant messages and files are accepted only from durable seats in an active room. A file is opaque binary data: cowork neither interprets its MIME metadata nor executes its contents. Its filename must be a path-free name of at most 255 UTF-8 bytes (not `.`, `..`, or a name containing `/`, `\\`, or NUL); MIME metadata may be empty and is limited to 255 UTF-8 bytes. Zero-byte files are valid. The maximum file size is 2 MiB (2,097,152 bytes), and a larger file is rejected explicitly.
43
43
 
44
+ Every participant-facing version-one room envelope includes the authenticated
45
+ top-level `room_name`, including messages, file metadata, and the
46
+ `room_not_member` notice. It is the room's validated presentation name (trimmed,
47
+ Unicode NFC, 1–64 characters, with control and format characters rejected), so
48
+ clients can render the task/room title without deriving it from the SDK identity
49
+ name, slug, or room ID.
50
+
44
51
  Incoming message listings contain authorization metadata but no body. Cowork resolves each selected wire ID through persistent SDK history, verifies that the history row matches the listing, archives the message and complete recipient-intent fan-out, and then asks the SDK to read one oldest unread row. If an older introduction becomes unread between listing and acknowledgement, that authoritative row is archived through the same intake path before cowork retries the expected row. There is no defer operation; an empty acknowledgement means another reader already advanced the expected row.
45
52
 
46
53
  For a file, cowork reads the identity-scoped immutable blob while the item is still unread and verifies its length and SHA-256 against SDK metadata. It then durably archives the exact bytes as canonical base64 together with the reply reference and every per-recipient relay intent before selecting that exact 64-hex wire ID with `getFiles`. Each other active seat receives two SDK items: an authenticated `room_file` metadata envelope and a binary file containing the original bytes. The SDK receives bytes, never a local filesystem path, so there is no staging-file or path-permission dependency. A seat removed after fan-out is skipped terminally without receiving metadata or bytes. A crash can redrive an unfinished recipient from the cowork archive; an already terminal recipient is not retried.
@@ -6,9 +6,9 @@ Back up the complete cowork state directory as one unit, preserving ownership an
6
6
 
7
7
  For restore, stop cowork, replace the complete cowork state directory with the complete backup, restore its original owner and `0700`/`0600` permissions, ensure the shared daemon already contains the corresponding room identities, and then start cowork. Do not merge individual room directories from different snapshots. Restore to a compatible package version and verify `ours-cowork status` plus representative `room show` and `room history` calls.
8
8
 
9
- An established standard room restores only by choosing its exact persisted `identity_name`—either the stable `ours-cowork-<room_id>` form or the friendly `ours-cowork-<bounded-ascii-slug>-<room_id>` form—with `force: false` and verifying the exact durable identity CID. Neither the current naming configuration nor the mutable `room_name` participates in restore. If that exact identity is absent from the shared daemon or its CID differs, startup fails clearly; cowork never renames, adopts, or recreates an established identity. Only a durable `packet_pending` room may create a missing identity.
9
+ An established room restores only by choosing its exact persisted `ours-cowork:<normalized creation name>` identity with `force: false` and verifying the exact durable identity CID. Mutable `room_name` metadata does not participate in restore or Messenger display. If that exact identity is absent from the shared daemon or its CID differs, startup fails clearly; cowork never renames, adopts, or recreates an established identity. Only a durable `packet_pending` room may create a missing identity.
10
10
 
11
- A binary from before friendly identity support cannot open a room created with a friendly identity name. It fails closed during standard-identity validation and must not rename or recreate the identity. Switching `roomIdentity.nameMode` back to `stable_id` affects only rooms created afterward; an existing friendly room requires this supporting release or a later one. Re-upgrading restores the room through its exact persisted friendly name and CID. There is no automatic down-migration.
11
+ Earlier unreleased ID- and slug-based identity formats are rejected. This unreleased major provides no migration or down-migration.
12
12
 
13
13
  Metadata using the exact legacy `cowork-room-<room_id>` form remains loadable so cowork can identify it and provide explicit migration guidance, but it is not a standard-restorable identity. Pre-1.0 custom packet state is refused rather than upgraded, renamed, or recreated in place: back it up with its compatible old release, recreate the room, and re-invite its participants.
14
14
 
@@ -15,7 +15,7 @@
15
15
  - The web console and HTTP room RPC have no authentication. They bind only to `127.0.0.1` and must not be forwarded, proxied, or exposed remotely.
16
16
  - Web updates use periodic polling rather than push. A view can lag daemon state until its next refresh; confirmed mutations trigger an immediate refresh.
17
17
  - Browser state is transient apart from the selected-room URL hash. Invite receipts disappear when closed and are not recoverable from browser storage.
18
- - Room names are not unique. New rooms use the globally unique authenticated identity `ours-cowork-<room_id>` by default, or `ours-cowork-<bounded-ascii-slug>-<room_id>` in configured friendly mode, while `room_name` remains editable. The exact identity name freezes at creation, so a room rename or later configuration rollback does not rename Messenger contacts. Interfaces must distinguish duplicate or renamed rooms by CID/room ID and may use local display aliases; an alias does not change authenticated provenance.
18
+ - Room identity names are daemon-global. Creating another room with the same normalized name collides and fails closed. Each identity is exactly `ours-cowork:<normalized creation name>` and freezes at creation, so a later `room_name` edit does not rename Messenger contacts. Interfaces distinguish renamed rooms by CID/room ID; local aliases do not change authenticated provenance.
19
19
  - Fresh and packet-pending provisioning refuses to adopt a same-name identity without a durably recorded expected CID. A crash after shared-daemon identity creation but before cowork's CID save can therefore leave an unproven orphan that prevents startup. After verifying no cowork metadata records its CID, an operator must remove that exact orphan with standard ours identity tooling and restart cowork. This destructive recovery is intentionally manual and fail-closed.
20
20
  - Cowork selects room identities by the exact names stored in its local room records. That filter prevents unrelated daemon-global identities from appearing as cowork rooms, but it is bookkeeping rather than an ownership, provenance, membership, or same-user security model.
21
21
  - Pre-1.0 custom room actor state cannot be opened by the standard SDK runtime. Back it up with the old release, recreate the room, and re-invite its participants.
@@ -25,12 +25,12 @@ Every room operation is carried by the single route `POST /rpc` with the envelop
25
25
 
26
26
  1. Choose Create room and enter the friendly Name, Goal, and Briefing. The name is shown in the room list, workspace header, and room details.
27
27
  2. Submit once. The new room is selected and its Invite panel opens.
28
- 3. Add one invitation requirement at a time. Choose one-time or public mode and set the minimum acceptances for a public invite. Revoke or consume it before creating another; the standard-SDK room keeps only one live invite so contact admission remains unambiguous.
28
+ 3. Add one or more invitation requirements. Choose one-time or public mode and set the minimum acceptances for a public invite. Each contact is admitted into the exact role authenticated by the invite it redeemed. An older SDK without invite provenance will ask you to revoke or consume the current invite before creating another.
29
29
  4. Copy every invite from its blocking receipt before choosing Done. The secret is shown once and is not retained in browser storage, the URL, logs, or durable room metadata.
30
30
 
31
31
  The room activates after its durable invitation requirements are satisfied. Participants shows admitted identities and their invite roles.
32
32
 
33
- Friendly names are trimmed and Unicode NFC-normalized, must contain 1–64 Unicode characters, and cannot contain Unicode control or format characters. Duplicate names are allowed. Change a name in Room settings. The browser continues to route by the opaque room ID; renaming does not change the URL, storage key, or underlying room identity. Existing unnamed rooms appear as `Room <first 8 room_id characters>`.
33
+ Names are trimmed and Unicode NFC-normalized, must contain 1–64 Unicode characters, and cannot contain Unicode control or format characters. At creation, the normalized name must not collide with an identity already in the shared daemon. Change local room metadata in Room settings. The browser continues to route by the opaque room ID; renaming does not change the URL, storage key, or underlying `ours-cowork:<creation name>` identity. Unnamed creation requests use `Room <first 8 room_id characters>`.
34
34
 
35
35
  ## Communication and records
36
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/cowork",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Durable ours mission rooms on the shared ours daemon.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",
@@ -26,14 +26,14 @@
26
26
  "test:release": "node --test tests/static-gates.test.mjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ours.network/sdk": "3.0.1",
29
+ "@ours.network/sdk": "3.5.0",
30
30
  "react": "18.3.1",
31
31
  "react-dom": "18.3.1",
32
32
  "zod": "^3.23.8"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@adapt-toolkit/broker": "0.10.12",
36
- "@ours.network/cli": "2.0.1",
36
+ "@ours.network/cli": "2.5.0",
37
37
  "@testing-library/jest-dom": "6.6.3",
38
38
  "@testing-library/react": "16.1.0",
39
39
  "@testing-library/user-event": "14.5.2",