@joe-broadhead/messaging 0.0.0-stage → 0.7.1
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.
- package/CHANGELOG.md +220 -0
- package/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +36 -2
- package/dist/bin/messaging-mcp.js +26524 -0
- package/dist/bin/messaging-relay.js +19768 -0
- package/dist/bin/messaging.js +15465 -0
- package/dist/bin/messagingd.js +29449 -0
- package/dist/lib/core-testing.js +1 -0
- package/dist/lib/core.js +1 -0
- package/dist/lib/ipc-testing.js +1 -0
- package/dist/lib/ipc.js +1 -0
- package/dist/lib/modules/core/address.js +42 -0
- package/dist/lib/modules/core/agent/operations.js +595 -0
- package/dist/lib/modules/core/agent/present.js +244 -0
- package/dist/lib/modules/core/agent/text.js +79 -0
- package/dist/lib/modules/core/agent-tools.js +477 -0
- package/dist/lib/modules/core/b64u.js +20 -0
- package/dist/lib/modules/core/cards.js +55 -0
- package/dist/lib/modules/core/check.js +20 -0
- package/dist/lib/modules/core/consent.js +199 -0
- package/dist/lib/modules/core/contact-card.js +74 -0
- package/dist/lib/modules/core/conversation/context.js +80 -0
- package/dist/lib/modules/core/conversation/event.js +100 -0
- package/dist/lib/modules/core/conversation/ids.js +38 -0
- package/dist/lib/modules/core/conversation/link.js +35 -0
- package/dist/lib/modules/core/conversation/log.js +766 -0
- package/dist/lib/modules/core/conversation/ratchet.js +243 -0
- package/dist/lib/modules/core/conversation/seal.js +198 -0
- package/dist/lib/modules/core/delegation.js +174 -0
- package/dist/lib/modules/core/directory/device-log.js +258 -0
- package/dist/lib/modules/core/directory/epoch.js +103 -0
- package/dist/lib/modules/core/directory/handle.js +29 -0
- package/dist/lib/modules/core/directory/jelly-device.js +204 -0
- package/dist/lib/modules/core/directory/jelly-issuer.js +12 -0
- package/dist/lib/modules/core/directory/jelly-token.js +305 -0
- package/dist/lib/modules/core/directory/keys-statement.js +43 -0
- package/dist/lib/modules/core/directory/link.js +81 -0
- package/dist/lib/modules/core/directory/merkle-log.js +250 -0
- package/dist/lib/modules/core/directory/person-view.js +197 -0
- package/dist/lib/modules/core/directory/person.js +40 -0
- package/dist/lib/modules/core/directory/realm.js +59 -0
- package/dist/lib/modules/core/directory/smt.js +266 -0
- package/dist/lib/modules/core/envelope.js +320 -0
- package/dist/lib/modules/core/hash.js +5 -0
- package/dist/lib/modules/core/head.js +27 -0
- package/dist/lib/modules/core/identity.js +71 -0
- package/dist/lib/modules/core/index.js +53 -0
- package/dist/lib/modules/core/jcs.js +96 -0
- package/dist/lib/modules/core/jelly-host-double.js +35 -0
- package/dist/lib/modules/core/jelly-host.js +211 -0
- package/dist/lib/modules/core/keys.js +79 -0
- package/dist/lib/modules/core/peer-text.js +563 -0
- package/dist/lib/modules/core/realm.js +1 -0
- package/dist/lib/modules/core/roster.js +454 -0
- package/dist/lib/modules/core/schema/a2a.js +85 -0
- package/dist/lib/modules/core/schema/common.js +310 -0
- package/dist/lib/modules/core/schema/consent.js +120 -0
- package/dist/lib/modules/core/schema/conversation.js +292 -0
- package/dist/lib/modules/core/schema/delegation.js +58 -0
- package/dist/lib/modules/core/schema/directory-frames.js +270 -0
- package/dist/lib/modules/core/schema/directory.js +169 -0
- package/dist/lib/modules/core/schema/envelope.js +211 -0
- package/dist/lib/modules/core/schema/errors.js +104 -0
- package/dist/lib/modules/core/schema/index.js +16 -0
- package/dist/lib/modules/core/schema/ipc/consent.js +181 -0
- package/dist/lib/modules/core/schema/ipc/device.js +276 -0
- package/dist/lib/modules/core/schema/ipc/jelly.js +189 -0
- package/dist/lib/modules/core/schema/ipc/shapes.js +185 -0
- package/dist/lib/modules/core/schema/ipc/surfaces.js +223 -0
- package/dist/lib/modules/core/schema/ipc.js +451 -0
- package/dist/lib/modules/core/schema/jelly.js +122 -0
- package/dist/lib/modules/core/schema/realm.js +27 -0
- package/dist/lib/modules/core/schema/relay.js +112 -0
- package/dist/lib/modules/core/schema/roster.js +126 -0
- package/dist/lib/modules/core/sign.js +43 -0
- package/dist/lib/modules/core/task.js +37 -0
- package/dist/lib/modules/core/testing.js +21 -0
- package/dist/lib/modules/core/ulid.js +36 -0
- package/dist/lib/modules/core/version.js +6 -0
- package/dist/lib/modules/core/wire.js +255 -0
- package/dist/lib/modules/ipc/client.js +289 -0
- package/dist/lib/modules/ipc/framing.js +33 -0
- package/dist/lib/modules/ipc/index.js +6 -0
- package/dist/lib/modules/ipc/paths.js +102 -0
- package/dist/lib/modules/ipc/pipes.js +65 -0
- package/dist/lib/modules/ipc/rpc.js +8 -0
- package/dist/lib/modules/ipc/server.js +317 -0
- package/dist/lib/modules/ipc/testing.js +145 -0
- package/dist/lib/modules/ipc/trust.js +166 -0
- package/dist/lib/modules/ipc/windows-acl.js +275 -0
- package/dist/lib/modules/vendor/zod/index.js +7 -0
- package/dist/lib/modules/vendor/zod/v4/classic/checks.js +1 -0
- package/dist/lib/modules/vendor/zod/v4/classic/coerce.js +17 -0
- package/dist/lib/modules/vendor/zod/v4/classic/compat.js +31 -0
- package/dist/lib/modules/vendor/zod/v4/classic/deep-partial.js +13 -0
- package/dist/lib/modules/vendor/zod/v4/classic/errors.js +57 -0
- package/dist/lib/modules/vendor/zod/v4/classic/external.js +17 -0
- package/dist/lib/modules/vendor/zod/v4/classic/from-json-schema.js +881 -0
- package/dist/lib/modules/vendor/zod/v4/classic/in-out.js +36 -0
- package/dist/lib/modules/vendor/zod/v4/classic/iso.js +15 -0
- package/dist/lib/modules/vendor/zod/v4/classic/parse.js +16 -0
- package/dist/lib/modules/vendor/zod/v4/classic/schemas.js +1535 -0
- package/dist/lib/modules/vendor/zod/v4/core/api.js +1107 -0
- package/dist/lib/modules/vendor/zod/v4/core/checks.js +516 -0
- package/dist/lib/modules/vendor/zod/v4/core/compile.js +1866 -0
- package/dist/lib/modules/vendor/zod/v4/core/core.js +141 -0
- package/dist/lib/modules/vendor/zod/v4/core/doc.js +38 -0
- package/dist/lib/modules/vendor/zod/v4/core/errors.js +267 -0
- package/dist/lib/modules/vendor/zod/v4/core/index.js +19 -0
- package/dist/lib/modules/vendor/zod/v4/core/json-schema-generator.js +99 -0
- package/dist/lib/modules/vendor/zod/v4/core/json-schema-processors.js +843 -0
- package/dist/lib/modules/vendor/zod/v4/core/json-schema.js +1 -0
- package/dist/lib/modules/vendor/zod/v4/core/memoizer.js +311 -0
- package/dist/lib/modules/vendor/zod/v4/core/parse.js +175 -0
- package/dist/lib/modules/vendor/zod/v4/core/regexes.js +156 -0
- package/dist/lib/modules/vendor/zod/v4/core/registries.js +51 -0
- package/dist/lib/modules/vendor/zod/v4/core/schemas.js +2636 -0
- package/dist/lib/modules/vendor/zod/v4/core/to-json-schema.js +651 -0
- package/dist/lib/modules/vendor/zod/v4/core/util.js +873 -0
- package/dist/lib/modules/vendor/zod/v4/core/versions.js +5 -0
- package/dist/lib/modules/vendor/zod/v4/core/visit.js +169 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ar.js +111 -0
- package/dist/lib/modules/vendor/zod/v4/locales/az.js +110 -0
- package/dist/lib/modules/vendor/zod/v4/locales/be.js +168 -0
- package/dist/lib/modules/vendor/zod/v4/locales/bg.js +125 -0
- package/dist/lib/modules/vendor/zod/v4/locales/bn.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ca.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ckb.js +133 -0
- package/dist/lib/modules/vendor/zod/v4/locales/cs.js +116 -0
- package/dist/lib/modules/vendor/zod/v4/locales/da.js +120 -0
- package/dist/lib/modules/vendor/zod/v4/locales/de.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/el.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/en.js +125 -0
- package/dist/lib/modules/vendor/zod/v4/locales/eo.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/es.js +136 -0
- package/dist/lib/modules/vendor/zod/v4/locales/fa.js +119 -0
- package/dist/lib/modules/vendor/zod/v4/locales/fi.js +117 -0
- package/dist/lib/modules/vendor/zod/v4/locales/fr-CA.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/fr.js +129 -0
- package/dist/lib/modules/vendor/zod/v4/locales/gu.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/he.js +221 -0
- package/dist/lib/modules/vendor/zod/v4/locales/hi.js +111 -0
- package/dist/lib/modules/vendor/zod/v4/locales/hr.js +126 -0
- package/dist/lib/modules/vendor/zod/v4/locales/hu.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/hy.js +158 -0
- package/dist/lib/modules/vendor/zod/v4/locales/id.js +111 -0
- package/dist/lib/modules/vendor/zod/v4/locales/index.js +63 -0
- package/dist/lib/modules/vendor/zod/v4/locales/is.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/it.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ja.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ka.js +117 -0
- package/dist/lib/modules/vendor/zod/v4/locales/kh.js +5 -0
- package/dist/lib/modules/vendor/zod/v4/locales/km.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/kn.js +116 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ko.js +116 -0
- package/dist/lib/modules/vendor/zod/v4/locales/lt.js +207 -0
- package/dist/lib/modules/vendor/zod/v4/locales/mk.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ms.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ne.js +111 -0
- package/dist/lib/modules/vendor/zod/v4/locales/nl.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/nn.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/no.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ota.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/pl.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ps.js +119 -0
- package/dist/lib/modules/vendor/zod/v4/locales/pt-BR.js +143 -0
- package/dist/lib/modules/vendor/zod/v4/locales/pt.js +142 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ro.js +122 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ru.js +168 -0
- package/dist/lib/modules/vendor/zod/v4/locales/sk.js +116 -0
- package/dist/lib/modules/vendor/zod/v4/locales/sl.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/sv.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ta.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/tg.js +117 -0
- package/dist/lib/modules/vendor/zod/v4/locales/th.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/tk.js +107 -0
- package/dist/lib/modules/vendor/zod/v4/locales/tr.js +110 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ua.js +5 -0
- package/dist/lib/modules/vendor/zod/v4/locales/uk.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/ur.js +115 -0
- package/dist/lib/modules/vendor/zod/v4/locales/uz.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/vi.js +113 -0
- package/dist/lib/modules/vendor/zod/v4/locales/yo.js +112 -0
- package/dist/lib/modules/vendor/zod/v4/locales/zh-CN.js +114 -0
- package/dist/lib/modules/vendor/zod/v4/locales/zh-TW.js +112 -0
- package/dist/lib/testing-jelly-id.js +1219 -0
- package/dist/lib/zod.js +1 -0
- package/dist/spec/messaging-v1.md +3744 -0
- package/dist/third-party-notices.md +239 -0
- package/dist/third-party.json +88 -0
- package/dist/types/core/address.d.ts +16 -0
- package/dist/types/core/agent/operations.d.ts +129 -0
- package/dist/types/core/agent/present.d.ts +57 -0
- package/dist/types/core/agent/text.d.ts +62 -0
- package/dist/types/core/agent-tools.d.ts +966 -0
- package/dist/types/core/b64u.d.ts +6 -0
- package/dist/types/core/cards.d.ts +21 -0
- package/dist/types/core/check.d.ts +10 -0
- package/dist/types/core/consent.d.ts +132 -0
- package/dist/types/core/contact-card.d.ts +34 -0
- package/dist/types/core/conversation/context.d.ts +56 -0
- package/dist/types/core/conversation/event.d.ts +36 -0
- package/dist/types/core/conversation/ids.d.ts +18 -0
- package/dist/types/core/conversation/link.d.ts +16 -0
- package/dist/types/core/conversation/log.d.ts +142 -0
- package/dist/types/core/conversation/ratchet.d.ts +95 -0
- package/dist/types/core/conversation/seal.d.ts +47 -0
- package/dist/types/core/delegation.d.ts +71 -0
- package/dist/types/core/directory/device-log.d.ts +119 -0
- package/dist/types/core/directory/epoch.d.ts +53 -0
- package/dist/types/core/directory/handle.d.ts +11 -0
- package/dist/types/core/directory/jelly-device.d.ts +88 -0
- package/dist/types/core/directory/jelly-issuer.d.ts +8 -0
- package/dist/types/core/directory/jelly-token.d.ts +116 -0
- package/dist/types/core/directory/keys-statement.d.ts +22 -0
- package/dist/types/core/directory/link.d.ts +36 -0
- package/dist/types/core/directory/merkle-log.d.ts +40 -0
- package/dist/types/core/directory/person-view.d.ts +90 -0
- package/dist/types/core/directory/person.d.ts +8 -0
- package/dist/types/core/directory/realm.d.ts +23 -0
- package/dist/types/core/directory/smt.d.ts +52 -0
- package/dist/types/core/envelope.d.ts +68 -0
- package/dist/types/core/hash.d.ts +2 -0
- package/dist/types/core/head.d.ts +34 -0
- package/dist/types/core/identity.d.ts +35 -0
- package/dist/types/core/index.d.ts +45 -0
- package/dist/types/core/jcs.d.ts +12 -0
- package/dist/types/core/jelly-host-double.d.ts +21 -0
- package/dist/types/core/jelly-host.d.ts +86 -0
- package/dist/types/core/keys.d.ts +16 -0
- package/dist/types/core/peer-text.d.ts +87 -0
- package/dist/types/core/realm.d.ts +36 -0
- package/dist/types/core/roster.d.ts +146 -0
- package/dist/types/core/schema/a2a.d.ts +251 -0
- package/dist/types/core/schema/common.d.ts +328 -0
- package/dist/types/core/schema/consent.d.ts +140 -0
- package/dist/types/core/schema/conversation.d.ts +1657 -0
- package/dist/types/core/schema/delegation.d.ts +58 -0
- package/dist/types/core/schema/directory-frames.d.ts +1301 -0
- package/dist/types/core/schema/directory.d.ts +375 -0
- package/dist/types/core/schema/envelope.d.ts +756 -0
- package/dist/types/core/schema/errors.d.ts +142 -0
- package/dist/types/core/schema/index.d.ts +16 -0
- package/dist/types/core/schema/ipc/consent.d.ts +555 -0
- package/dist/types/core/schema/ipc/device.d.ts +735 -0
- package/dist/types/core/schema/ipc/jelly.d.ts +501 -0
- package/dist/types/core/schema/ipc/shapes.d.ts +414 -0
- package/dist/types/core/schema/ipc/surfaces.d.ts +680 -0
- package/dist/types/core/schema/ipc.d.ts +3146 -0
- package/dist/types/core/schema/jelly.d.ts +160 -0
- package/dist/types/core/schema/realm.d.ts +38 -0
- package/dist/types/core/schema/relay.d.ts +1134 -0
- package/dist/types/core/schema/roster.d.ts +347 -0
- package/dist/types/core/sign.d.ts +13 -0
- package/dist/types/core/task.d.ts +6 -0
- package/dist/types/core/testing.d.ts +19 -0
- package/dist/types/core/ulid.d.ts +5 -0
- package/dist/types/core/version.d.ts +6 -0
- package/dist/types/core/wire.d.ts +14 -0
- package/dist/types/ipc/client.d.ts +85 -0
- package/dist/types/ipc/framing.d.ts +12 -0
- package/dist/types/ipc/index.d.ts +6 -0
- package/dist/types/ipc/paths.d.ts +39 -0
- package/dist/types/ipc/pipes.d.ts +16 -0
- package/dist/types/ipc/rpc.d.ts +13 -0
- package/dist/types/ipc/server.d.ts +94 -0
- package/dist/types/ipc/testing.d.ts +89 -0
- package/dist/types/ipc/trust.d.ts +49 -0
- package/dist/types/ipc/windows-acl.d.ts +56 -0
- package/dist/types/testing/jelly-id.d.ts +518 -0
- package/dist/types/zod.d.ts +1 -0
- package/package.json +77 -4
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted realms: the key directory's signed and hashed objects (spec §15). Every object here is hashed or
|
|
3
|
+
* signed as JCS, so each is strict: an unknown member is refused rather than carried unsigned.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "../../vendor/zod/index.js";
|
|
6
|
+
import { Base64Url, EncKey, Hash, HostedPersonId, Key32, LIMITS, MachineId, MachineLabel, Millis, PROTOCOL } from "./common.js";
|
|
7
|
+
import { Jws } from "./jelly.js";
|
|
8
|
+
import { RealmId } from "./realm.js";
|
|
9
|
+
/** A map proof (§15.5): a 256-bit bitmap of the non-empty siblings, and those siblings, bottom up. */
|
|
10
|
+
export const MapProof = z.strictObject({
|
|
11
|
+
bitmap: Base64Url.length(43),
|
|
12
|
+
siblings: z.array(Hash).max(256),
|
|
13
|
+
});
|
|
14
|
+
/** What a live person's map leaf commits to (§15.5): their log head, and the hashes of their current keys statements. */
|
|
15
|
+
export const MapValue = z.strictObject({
|
|
16
|
+
log: z.strictObject({ seq: z.number().int().nonnegative(), hash: Hash }),
|
|
17
|
+
keys: z.array(Hash).max(LIMITS.devicesPerPerson),
|
|
18
|
+
});
|
|
19
|
+
/** A device's id: its Ed25519 public key, as a machine's (§3). */
|
|
20
|
+
export const DeviceId = MachineId.describe("Ed25519 public key of a device, base64url");
|
|
21
|
+
/** What kind of device it is; informational. */
|
|
22
|
+
export const DeviceKind = z.enum(["jelly-host", "laptop", "server", "other"]);
|
|
23
|
+
/** Why a device was unbound (§15.3). */
|
|
24
|
+
export const UnbindReason = z.enum(["self", "removed", "lost", "replaced", "account_deleted"]);
|
|
25
|
+
/** An Ed25519 signature: 64 bytes, base64url. */
|
|
26
|
+
const Signature = z.string().regex(/^[A-Za-z0-9_-]{86}$/, "64 bytes, base64url without padding");
|
|
27
|
+
/**
|
|
28
|
+
* A new device's request to be approved by one of its person's devices (§15.3): shown as
|
|
29
|
+
* `messaging-link.<base64url(JCS(request))>`. `sig` is the new device's, over JCS(request without sig).
|
|
30
|
+
*/
|
|
31
|
+
export const LinkRequest = z.strictObject({
|
|
32
|
+
v: z.literal(PROTOCOL),
|
|
33
|
+
realm: RealmId,
|
|
34
|
+
device: DeviceId,
|
|
35
|
+
enc: EncKey,
|
|
36
|
+
label: MachineLabel,
|
|
37
|
+
kind: DeviceKind,
|
|
38
|
+
nonce: z.string().regex(/^[A-Za-z0-9_-]{22}$/, "16 bytes, base64url"),
|
|
39
|
+
ts: Millis,
|
|
40
|
+
sig: Signature,
|
|
41
|
+
});
|
|
42
|
+
/** Who vouches for a device-log entry (§15.3). */
|
|
43
|
+
export const Authority = z.discriminatedUnion("kind", [
|
|
44
|
+
/** Jelly ID, by a resource token bound (`cnf`) to the signing device. */
|
|
45
|
+
z.strictObject({ kind: z.literal("jelly"), token: Jws }),
|
|
46
|
+
/** One of the person's Jelly-vouched devices, approving the link request of a new one; the entry's `by`. */
|
|
47
|
+
z.strictObject({ kind: z.literal("device"), approver: DeviceId, link: LinkRequest }),
|
|
48
|
+
/** The directory, by its epoch key: only the unbinds of an erased account. */
|
|
49
|
+
z.strictObject({ kind: z.literal("service") }),
|
|
50
|
+
/** The device itself: only its own unbind. */
|
|
51
|
+
z.strictObject({ kind: z.literal("self") }),
|
|
52
|
+
]);
|
|
53
|
+
/**
|
|
54
|
+
* One entry of a hosted person's device log (§15.3), hash-chained by `prev` and signed by `by` over
|
|
55
|
+
* JCS(entry without sig) with `DOMAIN.deviceLog`. `bind` names the device's `label` and `kind`; `attest` may
|
|
56
|
+
* change them; `unbind` gives a `reason` and nothing else.
|
|
57
|
+
*/
|
|
58
|
+
export const LogEntry = z
|
|
59
|
+
.strictObject({
|
|
60
|
+
v: z.literal(PROTOCOL),
|
|
61
|
+
realm: RealmId,
|
|
62
|
+
person: HostedPersonId,
|
|
63
|
+
seq: z.number().int().nonnegative(),
|
|
64
|
+
prev: Hash.nullable(),
|
|
65
|
+
ts: Millis,
|
|
66
|
+
op: z.enum(["bind", "attest", "unbind"]),
|
|
67
|
+
device: DeviceId,
|
|
68
|
+
label: MachineLabel.optional(),
|
|
69
|
+
kind: DeviceKind.optional(),
|
|
70
|
+
reason: UnbindReason.optional(),
|
|
71
|
+
authority: Authority,
|
|
72
|
+
by: Key32,
|
|
73
|
+
sig: Signature,
|
|
74
|
+
})
|
|
75
|
+
.refine((entry) => (entry.seq === 0) === (entry.prev === null), { message: "prev is null exactly at seq 0", path: ["prev"] })
|
|
76
|
+
.refine((entry) => entry.op !== "bind" || (entry.label !== undefined && entry.kind !== undefined), { message: "a bind names label and kind", path: ["label"] })
|
|
77
|
+
.refine((entry) => (entry.op === "unbind") === (entry.reason !== undefined), { message: "an unbind, and only an unbind, gives a reason", path: ["reason"] })
|
|
78
|
+
.refine((entry) => entry.op !== "unbind" || (entry.label === undefined && entry.kind === undefined), { message: "an unbind names no label or kind", path: ["label"] });
|
|
79
|
+
/**
|
|
80
|
+
* A device's self-signed encryption key (§15.4): the directory keeps the latest per device (`seq` strictly
|
|
81
|
+
* increases). `sig` is the device's, over JCS(statement without sig) with `DOMAIN.deviceKeys`.
|
|
82
|
+
*/
|
|
83
|
+
export const KeysStatement = z.strictObject({
|
|
84
|
+
v: z.literal(PROTOCOL),
|
|
85
|
+
realm: RealmId,
|
|
86
|
+
person: HostedPersonId,
|
|
87
|
+
device: DeviceId,
|
|
88
|
+
enc: EncKey,
|
|
89
|
+
seq: z.number().int().nonnegative(),
|
|
90
|
+
ts: Millis,
|
|
91
|
+
sig: Signature,
|
|
92
|
+
});
|
|
93
|
+
/**
|
|
94
|
+
* One leaf of the epoch log (§15.6): the map root of epoch `epoch` and of the epoch before it (`E[256]`, the
|
|
95
|
+
* empty map's root, before epoch 0). The leaf's data is UTF-8(JCS(leaf)).
|
|
96
|
+
*/
|
|
97
|
+
export const EpochLeaf = z.strictObject({
|
|
98
|
+
epoch: z.number().int().nonnegative(),
|
|
99
|
+
ts: Millis,
|
|
100
|
+
mapRoot: Hash,
|
|
101
|
+
prevMapRoot: Hash,
|
|
102
|
+
});
|
|
103
|
+
/** An epoch-log leaf and its RFC 9162 audit path in the tree of the STH it is sent with (§15.6). */
|
|
104
|
+
export const EpochProof = z.strictObject({
|
|
105
|
+
index: z.number().int().nonnegative(),
|
|
106
|
+
leaf: EpochLeaf,
|
|
107
|
+
inclusion: z.array(Hash).max(64),
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* A signed tree head of the epoch log (§15.6): `size` leaves with root `root`, at `ts`. `sig` is the directory's
|
|
111
|
+
* epoch key's, over JCS(head without sig) with `DOMAIN.epoch`.
|
|
112
|
+
*/
|
|
113
|
+
export const STH = z.strictObject({
|
|
114
|
+
v: z.literal(PROTOCOL),
|
|
115
|
+
realm: RealmId,
|
|
116
|
+
size: z.number().int().positive(),
|
|
117
|
+
root: Hash,
|
|
118
|
+
ts: Millis,
|
|
119
|
+
sig: Signature,
|
|
120
|
+
});
|
|
121
|
+
/**
|
|
122
|
+
* The directory's epoch key moving from `old` to `new` (§15.6): STHs of `fromSize` leaves and more are signed by
|
|
123
|
+
* `new`. Each key signs JCS(rotation without its two signatures) with `DOMAIN.dirKey`.
|
|
124
|
+
*/
|
|
125
|
+
export const KeyRotation = z.strictObject({
|
|
126
|
+
v: z.literal(PROTOCOL),
|
|
127
|
+
realm: RealmId,
|
|
128
|
+
old: Key32,
|
|
129
|
+
new: Key32,
|
|
130
|
+
fromSize: z.number().int().positive(),
|
|
131
|
+
sigOld: Signature,
|
|
132
|
+
sigNew: Signature,
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* Evidence that log entry `seq` was in the directory by an epoch (§15.7): that epoch's leaf in the view's STH,
|
|
136
|
+
* and the person's map value there, which commits to a log head at or after `seq`.
|
|
137
|
+
*/
|
|
138
|
+
export const EntryEpoch = z.strictObject({
|
|
139
|
+
seq: z.number().int().nonnegative(),
|
|
140
|
+
epoch: EpochProof,
|
|
141
|
+
value: MapValue,
|
|
142
|
+
mapProof: MapProof,
|
|
143
|
+
});
|
|
144
|
+
/**
|
|
145
|
+
* One person's head as the directory serves it at an STH (§7, "Directory frames"; §15.7): the map proof of their value
|
|
146
|
+
* at the STH's last epoch, and that value's preimage (`value`: their log head and the hashes of their current devices'
|
|
147
|
+
* keys statements), or none for a person the map does not hold, or `tombstone: true` for an erased one. It holds no
|
|
148
|
+
* log entry, so no Jelly token and nothing of a profile: what a device about to bind needs, and what anyone may read.
|
|
149
|
+
*/
|
|
150
|
+
export const PersonHead = z.strictObject({
|
|
151
|
+
person: HostedPersonId,
|
|
152
|
+
mapProof: MapProof,
|
|
153
|
+
value: MapValue.optional(),
|
|
154
|
+
tombstone: z.literal(true).optional(),
|
|
155
|
+
});
|
|
156
|
+
/**
|
|
157
|
+
* One person as the directory serves them at an STH (§15.7): their log after the asker's `knownSeq` (all of it
|
|
158
|
+
* without one), the keys statements of their current devices, the map proof of their value at the STH's last
|
|
159
|
+
* epoch, and for every entry of `log` with a `jelly` authority, an `EntryEpoch` (its first epoch, from the
|
|
160
|
+
* directory's records). An erased person is `tombstone: true` with an empty log and no keys.
|
|
161
|
+
*/
|
|
162
|
+
export const PersonView = z.strictObject({
|
|
163
|
+
person: HostedPersonId,
|
|
164
|
+
log: z.array(LogEntry).max(1024),
|
|
165
|
+
keys: z.array(KeysStatement).max(LIMITS.devicesPerPerson),
|
|
166
|
+
mapProof: MapProof,
|
|
167
|
+
epochs: z.array(EntryEpoch).max(1024).optional(),
|
|
168
|
+
tombstone: z.literal(true).optional(),
|
|
169
|
+
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Messaging envelope: the end-to-end unit between machines. The sender signs it;
|
|
3
|
+
* every body except presence is sealed to the recipient machine's X25519 key.
|
|
4
|
+
* The relay can read the header (routing) but not the body.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "../../vendor/zod/index.js";
|
|
7
|
+
import { Message, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from "./a2a.js";
|
|
8
|
+
import { AgentKind, Base64Url, ChannelName, DeliveryTier, MachineId, MachineLabel, Millis, PersonId, PROTOCOL, SessionAddress, SessionName, SessionState, Ulid, LIMITS, } from "./common.js";
|
|
9
|
+
import { CardData, CardGetData, GrantSync } from "./consent.js";
|
|
10
|
+
import { DelegationChain } from "./delegation.js";
|
|
11
|
+
import { ErrorCode } from "./errors.js";
|
|
12
|
+
import { Anchor, RealmId } from "./realm.js";
|
|
13
|
+
export const EnvelopeType = z.enum(["message", "task.status", "task.artifact", "task.cancel", "receipt", "presence"]);
|
|
14
|
+
export const SEAL_ALG = "x25519-hkdf-sha256-a256gcm";
|
|
15
|
+
/**
|
|
16
|
+
* What the envelope is to its recipient's person (spec §5, "Class"), in the header so the signature and the AAD
|
|
17
|
+
* cover it: `self` between two devices of one person (`from.person` = `to.person`), `contact` between two persons,
|
|
18
|
+
* and `request` for a message request (spec §6.9, "Message requests"): a person's first line to a person of another
|
|
19
|
+
* org, which a directory relay paces by this header alone. Every sealed envelope carries it; presence does not.
|
|
20
|
+
*/
|
|
21
|
+
export const EnvelopeClass = z.enum(["self", "contact", "request"]);
|
|
22
|
+
/**
|
|
23
|
+
* One end of an envelope (spec §5, "Ends"): a person, one of their devices, and optionally an agent (a session) on
|
|
24
|
+
* that device. With no `agent`, the end is the person (or, for receipts and task control, the device acting for them).
|
|
25
|
+
*/
|
|
26
|
+
export const Party = z.object({
|
|
27
|
+
person: PersonId,
|
|
28
|
+
device: MachineId,
|
|
29
|
+
agent: SessionName.optional(),
|
|
30
|
+
});
|
|
31
|
+
/** Sender: the signing device, its person, and the agent that wrote the envelope, if an agent did. */
|
|
32
|
+
export const Sender = Party;
|
|
33
|
+
/** Recipient: one device of a person, and optionally an agent on it; presence alone goes to the whole team. */
|
|
34
|
+
export const Recipient = z.union([
|
|
35
|
+
Party,
|
|
36
|
+
z.object({ team: z.literal(true) }), // presence broadcast
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Wire form. `sig` = Ed25519(from.device, DOMAIN.envelope + JCS(envelope without `sig`)).
|
|
40
|
+
* Sealed: enc = SEAL_ALG, epk + nonce present, body = base64url(ciphertext||tag),
|
|
41
|
+
* AAD = UTF-8(JCS(header)), header = envelope without body and sig.
|
|
42
|
+
* Plain (presence only): enc = "none", body = payload object, no epk/nonce.
|
|
43
|
+
* `realm` is the sender's realm, and `anchor` its view of the realm when it sealed the envelope (spec §5,
|
|
44
|
+
* "Realm and anchor"): the signature covers both, and so does the AAD of a sealed envelope.
|
|
45
|
+
*/
|
|
46
|
+
export const Envelope = z.object({
|
|
47
|
+
v: z.literal(PROTOCOL),
|
|
48
|
+
id: Ulid,
|
|
49
|
+
realm: RealmId,
|
|
50
|
+
type: EnvelopeType,
|
|
51
|
+
from: Sender,
|
|
52
|
+
to: Recipient,
|
|
53
|
+
ts: Millis,
|
|
54
|
+
exp: Millis,
|
|
55
|
+
anchor: Anchor,
|
|
56
|
+
class: EnvelopeClass.optional(),
|
|
57
|
+
replyTo: Ulid.optional(),
|
|
58
|
+
enc: z.enum([SEAL_ALG, "none"]),
|
|
59
|
+
epk: Base64Url.optional(),
|
|
60
|
+
nonce: Base64Url.optional(),
|
|
61
|
+
body: z.unknown(),
|
|
62
|
+
sig: Base64Url,
|
|
63
|
+
});
|
|
64
|
+
// ---- Decrypted body payloads, by envelope type ----
|
|
65
|
+
/** A channel's persons: roster person names, or hosted `p_…` ids in a directory realm (spec §6.8). */
|
|
66
|
+
const ChannelPersons = z.array(PersonId).min(1).max(LIMITS.channelPersons);
|
|
67
|
+
/**
|
|
68
|
+
* A channel change or post (spec §6.8), on a `kind: "message"` envelope sealed to a device with no recipient agent.
|
|
69
|
+
* `create`: the full channel record with the person who created it, to every member device and to each later
|
|
70
|
+
* invitee. `invite`: persons added, to the existing members. `leave`: the sender left. `remove`: persons removed by
|
|
71
|
+
* the creator or an admin, to every member device. `post`: a line in the channel. `name` is always the channel's own
|
|
72
|
+
* name. A DM line has no `conv` (spec §5, "Meaning").
|
|
73
|
+
*/
|
|
74
|
+
export const ChannelConv = z.discriminatedUnion("op", [
|
|
75
|
+
z.object({ kind: z.literal("channel"), op: z.literal("create"), id: Ulid, name: ChannelName, persons: ChannelPersons, creator: PersonId }),
|
|
76
|
+
z.object({ kind: z.literal("channel"), op: z.literal("invite"), id: Ulid, name: ChannelName, persons: ChannelPersons }),
|
|
77
|
+
z.object({ kind: z.literal("channel"), op: z.literal("leave"), id: Ulid, name: ChannelName }),
|
|
78
|
+
z.object({ kind: z.literal("channel"), op: z.literal("remove"), id: Ulid, name: ChannelName, persons: ChannelPersons }),
|
|
79
|
+
z.object({ kind: z.literal("channel"), op: z.literal("post"), id: Ulid, name: ChannelName }),
|
|
80
|
+
]);
|
|
81
|
+
/**
|
|
82
|
+
* Sync between two devices of one person (spec §6.9, "Sync"): `grant` carries grants the person set or re-signed,
|
|
83
|
+
* `snapshot` every current grant, for a device of the person that is new to the sender. The message carries exactly
|
|
84
|
+
* one data part, a `GrantSync`.
|
|
85
|
+
*/
|
|
86
|
+
export const SelfConv = z.object({ kind: z.literal("self"), what: z.enum(["grant", "snapshot"]) });
|
|
87
|
+
/**
|
|
88
|
+
* A message request (spec §6.9, "Message requests"), on an envelope of class `request` from a person to a person:
|
|
89
|
+
* its message carries exactly one text part of at most `LIMITS.requestChars`.
|
|
90
|
+
*/
|
|
91
|
+
export const RequestConv = z.object({ kind: z.literal("request") });
|
|
92
|
+
/**
|
|
93
|
+
* A contact card between two persons (spec §6.9, "Contact cards"): `card` carries one data part `{card}`, the signed
|
|
94
|
+
* card; `get` asks for the receiver's card and carries one data part `{get: true}`.
|
|
95
|
+
*/
|
|
96
|
+
export const CardConv = z.object({ kind: z.literal("card"), what: z.enum(["card", "get"]) });
|
|
97
|
+
/** The conversation a `message` belongs to when its ends alone do not say (spec §5, "Meaning"). */
|
|
98
|
+
export const Conv = z.discriminatedUnion("kind", [ChannelConv, SelfConv, RequestConv, CardConv]);
|
|
99
|
+
/**
|
|
100
|
+
* type "message". `kind: "task"` asks the recipient to run `message` as a new A2A task;
|
|
101
|
+
* the sender assigns `message.taskId` (a ULID) and `message.contextId`.
|
|
102
|
+
* Replies to a task are task.status/task.artifact envelopes, not messages.
|
|
103
|
+
*/
|
|
104
|
+
export const MessagePayload = z
|
|
105
|
+
.object({
|
|
106
|
+
kind: z.enum(["message", "task"]),
|
|
107
|
+
message: Message,
|
|
108
|
+
/** Sender's hint that the recipient may wake an idle session. Recipient policy decides. */
|
|
109
|
+
wake: z.boolean().optional(),
|
|
110
|
+
/** Required on `kind: "task"`, absent otherwise: the signed chain from the root requester to this hop (spec §6.7). Receivers run `verifyChain`. */
|
|
111
|
+
delegation: DelegationChain.optional(),
|
|
112
|
+
/**
|
|
113
|
+
* The conversation, when the ends do not say it (spec §5, "Meaning"): a channel change or post (§6.8).
|
|
114
|
+
* `message.messageId` is a ULID shared by every copy; `message.contextId`, when set, is the ULID `messageId` of
|
|
115
|
+
* the thread's first line.
|
|
116
|
+
*/
|
|
117
|
+
conv: Conv.optional(),
|
|
118
|
+
})
|
|
119
|
+
.refine((p) => (p.kind === "task" ? p.message.taskId !== undefined : true), { message: "a task message needs message.taskId" })
|
|
120
|
+
.refine((p) => (p.kind === "task") === (p.delegation !== undefined), { message: "a task request carries a delegation chain, and only a task request" })
|
|
121
|
+
.refine((p) => p.conv?.kind !== "self" || (p.message.parts.length === 1 && GrantSync.safeParse(p.message.parts[0].data).success), {
|
|
122
|
+
message: "a self sync message carries exactly one data part with its grants",
|
|
123
|
+
})
|
|
124
|
+
.refine((p) => p.conv?.kind !== "request" || requestMessage(p.message), {
|
|
125
|
+
message: `a message request carries one text part of 1 to ${LIMITS.requestChars} characters, and nothing else`,
|
|
126
|
+
})
|
|
127
|
+
.refine((p) => p.conv?.kind !== "card" || cardMessage(p.message, p.conv.what), {
|
|
128
|
+
message: "a card message carries one data part, the card or the request for one, and nothing else",
|
|
129
|
+
})
|
|
130
|
+
.refine((p) => p.message.taskId === undefined || Ulid.safeParse(p.message.taskId).success, {
|
|
131
|
+
message: "Messaging task ids are ULIDs",
|
|
132
|
+
});
|
|
133
|
+
/** The members a message request or card message may have: no metadata, extensions or other text beside its one part. */
|
|
134
|
+
const BARE_MESSAGE = new Set(["messageId", "role", "parts"]);
|
|
135
|
+
const bare = (message, part) => Object.keys(message).every((key) => BARE_MESSAGE.has(key)) && message.parts.length === 1 && Object.keys(message.parts[0]).join() === part;
|
|
136
|
+
/** Spec §6.9, "Message requests": exactly one text part of 1 to `LIMITS.requestChars` UTF-16 code units, and nothing else. */
|
|
137
|
+
function requestMessage(message) {
|
|
138
|
+
const text = message.parts[0]?.text;
|
|
139
|
+
return bare(message, "text") && typeof text === "string" && text.length >= 1 && text.length <= LIMITS.requestChars;
|
|
140
|
+
}
|
|
141
|
+
/** Spec §6.9, "Contact cards": exactly one data part, `{card}` or `{get: true}`, and nothing else. */
|
|
142
|
+
function cardMessage(message, what) {
|
|
143
|
+
return bare(message, "data") && (what === "card" ? CardData : CardGetData).safeParse(message.parts[0].data).success;
|
|
144
|
+
}
|
|
145
|
+
/** type "task.status": executor → requester. */
|
|
146
|
+
export const TaskStatusPayload = TaskStatusUpdateEvent;
|
|
147
|
+
/** type "task.artifact": executor → requester. */
|
|
148
|
+
export const TaskArtifactPayload = TaskArtifactUpdateEvent;
|
|
149
|
+
/** type "task.cancel": requester → executor. The executor daemon moves the task to CANCELED if not terminal. */
|
|
150
|
+
export const TaskCancelPayload = z.object({
|
|
151
|
+
taskId: Ulid,
|
|
152
|
+
reason: z.string().max(1000).optional(),
|
|
153
|
+
});
|
|
154
|
+
/** type "receipt": recipient → sender, signed proof of a delivery stage for envelope `ref`. */
|
|
155
|
+
export const ReceiptPayload = z.object({
|
|
156
|
+
ref: Ulid,
|
|
157
|
+
stage: z.enum(["delivered", "held", "handed_to_agent", "read", "rejected"]),
|
|
158
|
+
code: ErrorCode.optional(),
|
|
159
|
+
note: z.string().max(500).optional(),
|
|
160
|
+
});
|
|
161
|
+
export const SessionInfo = z.object({
|
|
162
|
+
session: SessionName,
|
|
163
|
+
agent: AgentKind,
|
|
164
|
+
repo: z.string().max(200).optional(),
|
|
165
|
+
branch: z.string().max(200).optional(),
|
|
166
|
+
state: SessionState,
|
|
167
|
+
doing: z.string().max(200).optional(),
|
|
168
|
+
tier: DeliveryTier,
|
|
169
|
+
since: Millis,
|
|
170
|
+
/** No client holds the session: it is kept (spec §8.2) and its mail waits for it. */
|
|
171
|
+
kept: z.literal(true).optional(),
|
|
172
|
+
/** This machine would act on a sender's wake hint for the session now (spec §6.5, §8.3, §8.6). */
|
|
173
|
+
wake: z.literal(true).optional(),
|
|
174
|
+
});
|
|
175
|
+
/** type "presence": plain (not sealed), to {team:true}. Latest `seq` per machine wins. */
|
|
176
|
+
export const PresencePayload = z.object({
|
|
177
|
+
seq: z.number().int().nonnegative(),
|
|
178
|
+
label: MachineLabel,
|
|
179
|
+
sessions: z.array(SessionInfo).max(LIMITS.presenceSessions),
|
|
180
|
+
/** Optional protocol features this machine supports, e.g. "tasks". Unknown features are ignored. */
|
|
181
|
+
features: z.array(z.string().max(64)).max(32).optional(),
|
|
182
|
+
/**
|
|
183
|
+
* Under a Jelly host: no window of this device's person is visible on it (`person.attached`, spec §6.5, §8.11). Its
|
|
184
|
+
* agents still run; the person is away from this device. Absent: the person may be at it.
|
|
185
|
+
*/
|
|
186
|
+
away: z.literal(true).optional(),
|
|
187
|
+
});
|
|
188
|
+
export const PAYLOAD_BY_TYPE = {
|
|
189
|
+
message: MessagePayload,
|
|
190
|
+
"task.status": TaskStatusPayload,
|
|
191
|
+
"task.artifact": TaskArtifactPayload,
|
|
192
|
+
"task.cancel": TaskCancelPayload,
|
|
193
|
+
receipt: ReceiptPayload,
|
|
194
|
+
presence: PresencePayload,
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Provenance the receiving daemon attaches at `Message.metadata[URI.provenance]` before
|
|
198
|
+
* handing a message to any client. Clients show it; peers can never set it (the daemon overwrites it).
|
|
199
|
+
*/
|
|
200
|
+
export const Provenance = z.object({
|
|
201
|
+
envelopeId: Ulid,
|
|
202
|
+
from: SessionAddress.describe("sender address person/agent@label"),
|
|
203
|
+
person: PersonId,
|
|
204
|
+
/** The sending agent; absent when the person wrote it. */
|
|
205
|
+
agent: SessionName.optional(),
|
|
206
|
+
/** The sending device. */
|
|
207
|
+
device: MachineId,
|
|
208
|
+
label: MachineLabel,
|
|
209
|
+
verified: z.literal("signature"),
|
|
210
|
+
receivedAt: Millis,
|
|
211
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from "../../vendor/zod/index.js";
|
|
2
|
+
/** The closed set of Messaging error codes. Every emitted code must be documented in docs/spec/messaging-v1.md. */
|
|
3
|
+
export const ErrorCode = z.enum([
|
|
4
|
+
"invalid_request", // malformed input or schema violation
|
|
5
|
+
"not_member", // sender or recipient machine is not a current team member
|
|
6
|
+
"bad_signature", // signature or seal failed verification
|
|
7
|
+
"unknown_session", // recipient session does not exist on the target machine
|
|
8
|
+
"ambiguous_address", // address matches more than one session
|
|
9
|
+
"no_recipients", // address resolved to nothing
|
|
10
|
+
"unknown_address", // an agent tool's address names no person, agent or channel this machine knows (client-side; never on the wire)
|
|
11
|
+
"refused", // recipient policy refuses this sender
|
|
12
|
+
"declined", // the recipient's person declined a held request (spec §6.9)
|
|
13
|
+
"request_required", // the recipient's person has not accepted this sender: a message request comes first (spec §6.9)
|
|
14
|
+
"rate_limited", // too many envelopes; retry later
|
|
15
|
+
"loop_guard", // agent-to-agent loop guard tripped
|
|
16
|
+
"too_large", // size limit exceeded
|
|
17
|
+
"secret_refused", // the sending daemon found a credential in outbound text; nothing was stored or sent
|
|
18
|
+
"expired", // envelope TTL passed, or ts outside the skew window
|
|
19
|
+
"duplicate", // envelope id already seen (receivers treat as success)
|
|
20
|
+
"relay_unavailable", // no relay connection
|
|
21
|
+
"not_found", // unknown id
|
|
22
|
+
"invalid_state", // illegal task transition or wrong role for the operation
|
|
23
|
+
"forbidden", // caller lacks authority (e.g. non-admin roster change)
|
|
24
|
+
"guarded_turn", // a guarded peer turn tried to send outside its allowance (client-side; never on the wire)
|
|
25
|
+
"roster_conflict", // roster entry does not extend the current head; resync and retry
|
|
26
|
+
"invite_invalid", // unknown, expired, revoked, used or mismatched invite
|
|
27
|
+
"no_identity", // daemon has no identity/team yet
|
|
28
|
+
"binding_invalid", // a device binding, log entry or its authority failed verification (hosted realms)
|
|
29
|
+
"jelly_unavailable", // Jelly ID's key set or key history could not be fetched; never reported as invalid
|
|
30
|
+
"log_conflict", // a device-log entry does not extend the log's head; resync and retry
|
|
31
|
+
"conv_conflict", // a conversation event does not extend the log, or its op head is not the current one; re-read and retry
|
|
32
|
+
"stale_epoch", // a conversation message is sealed under an epoch that is not the current one, or a rekey is due; re-seal and retry
|
|
33
|
+
"device_limit", // the person already has LIMITS.devicesPerPerson current devices
|
|
34
|
+
"admission_required", // dir.bind without a Jelly-vouched bind of the connecting device
|
|
35
|
+
"admission_invalid", // dir.bind whose Jelly token is not live, or not for this directory's issuer and resource
|
|
36
|
+
"directory_inconsistent", // the directory showed a split view, a fork or an inconsistent tree head; evidence kept, alarm raised
|
|
37
|
+
"unknown_device", // a directory realm: the sender's device is not a current device of its person
|
|
38
|
+
"directory_unavailable", // the key directory could not be reached to verify a person's devices within the freshness bound
|
|
39
|
+
"login_unsupported", // Jelly ID offers no device sign-in for this realm's resource (no v1.2 device sign-in in its metadata)
|
|
40
|
+
"login_denied", // the person denied the sign-in at Jelly ID, or the account is disabled or being deleted
|
|
41
|
+
"grant_required", // deployment "jelly": a human- or admin-tier call carried no action grant (spec §8.11)
|
|
42
|
+
"grant_invalid", // deployment "jelly": the action grant did not verify, is for another call, expired or was spent
|
|
43
|
+
"ticket_required", // deployment "jelly": session.register carried no session ticket
|
|
44
|
+
"ticket_invalid", // deployment "jelly": the session ticket did not verify, expired, or names another session or agent
|
|
45
|
+
"daemon_unavailable", // client could not reach the local daemon socket
|
|
46
|
+
"daemon_mismatch", // the daemon on the socket is another version or IPC protocol than the client (spec §8.2)
|
|
47
|
+
"store_refused", // messagingd refused the home's store and changed nothing; StoreRefusedError.reason says why: pre-reset or newer (spec §8.8)
|
|
48
|
+
"internal", // unexpected failure; details are logged locally, never sent
|
|
49
|
+
]);
|
|
50
|
+
/**
|
|
51
|
+
* The exit status of a messagingd or messaging-relay that refuses its store and changes nothing (spec §8.8): `EX_CONFIG`
|
|
52
|
+
* of sysexits(3), for either reason. messagingd also prints one stderr line,
|
|
53
|
+
* `messagingd: store_refused: <reason>: <why and how to start over>`. A client matches this status, the code and the
|
|
54
|
+
* reason, never the wording.
|
|
55
|
+
*/
|
|
56
|
+
export const STORE_REFUSED_EXIT = 78;
|
|
57
|
+
/**
|
|
58
|
+
* Why a store is refused (spec §8.8):
|
|
59
|
+
* - `pre-reset`: it records no store generation, or another one. Starting over (moving the home aside) is the remedy.
|
|
60
|
+
* - `newer`: its `min_reader` is above this reader's schema: a newer messagingd wrote it. Upgrading is the remedy;
|
|
61
|
+
* moving the home aside would set a newer home aside.
|
|
62
|
+
*/
|
|
63
|
+
export const StoreRefusedReason = z.enum(["pre-reset", "newer"]);
|
|
64
|
+
export const ErrorBody = z.object({
|
|
65
|
+
code: ErrorCode,
|
|
66
|
+
message: z.string().max(500),
|
|
67
|
+
});
|
|
68
|
+
/** Error carrying a Messaging code. Messages must never contain bodies, keys or secrets. */
|
|
69
|
+
export class MessagingError extends Error {
|
|
70
|
+
code;
|
|
71
|
+
constructor(code, message, options) {
|
|
72
|
+
super(message, options);
|
|
73
|
+
this.name = "MessagingError";
|
|
74
|
+
this.code = code;
|
|
75
|
+
}
|
|
76
|
+
toBody() {
|
|
77
|
+
return { code: this.code, message: this.message.slice(0, 500) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** `store_refused`, with its reason as a field: clients branch on `reason`, never on the message. */
|
|
81
|
+
export class StoreRefusedError extends MessagingError {
|
|
82
|
+
reason;
|
|
83
|
+
constructor(reason, message, options) {
|
|
84
|
+
super("store_refused", message, options);
|
|
85
|
+
this.name = "StoreRefusedError";
|
|
86
|
+
this.reason = reason;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** The stderr line of a messagingd that refuses its store: `messagingd: store_refused: <reason>: <text>`. */
|
|
90
|
+
export function storeRefusedLine(reason, text) {
|
|
91
|
+
return `messagingd: store_refused: ${reason}: ${text}`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The reason on a messagingd refusal line, or undefined for any other line. Reads the tokens only; the text after
|
|
95
|
+
* them may change between releases.
|
|
96
|
+
*/
|
|
97
|
+
export function storeRefusedReason(line) {
|
|
98
|
+
const match = /^messagingd: store_refused: ([a-z-]+): /.exec(line);
|
|
99
|
+
const parsed = StoreRefusedReason.safeParse(match?.[1]);
|
|
100
|
+
return parsed.success ? parsed.data : undefined;
|
|
101
|
+
}
|
|
102
|
+
export function isMessagingError(value) {
|
|
103
|
+
return value instanceof MessagingError;
|
|
104
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from "./common.js";
|
|
2
|
+
export * from "./errors.js";
|
|
3
|
+
export * from "./realm.js";
|
|
4
|
+
export * from "./jelly.js";
|
|
5
|
+
export * from "./directory.js";
|
|
6
|
+
export * as A2A from "./a2a.js";
|
|
7
|
+
export * from "./envelope.js";
|
|
8
|
+
export * from "./delegation.js";
|
|
9
|
+
export * from "./consent.js";
|
|
10
|
+
export * from "./roster.js";
|
|
11
|
+
export * from "./relay.js";
|
|
12
|
+
export * from "./directory-frames.js";
|
|
13
|
+
export * from "./ipc.js";
|
|
14
|
+
export * from "./ipc/device.js";
|
|
15
|
+
export * from "./ipc/consent.js";
|
|
16
|
+
export * from "./conversation.js";
|