@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,6 @@
|
|
|
1
|
+
/** RFC 4648 §5 base64url without padding. `decode` rejects padding, foreign characters and non-canonical trailing bits. */
|
|
2
|
+
export declare const b64u: {
|
|
3
|
+
encode(bytes: Uint8Array): string;
|
|
4
|
+
decode(text: string): Uint8Array;
|
|
5
|
+
};
|
|
6
|
+
export declare function utf8(text: string): Uint8Array;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const TeamCard: z.ZodObject<{
|
|
3
|
+
v: z.ZodLiteral<"messaging/1">;
|
|
4
|
+
relay: z.ZodString;
|
|
5
|
+
team: z.ZodString;
|
|
6
|
+
name: z.ZodString;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
export type TeamCard = z.infer<typeof TeamCard>;
|
|
9
|
+
declare const AdmitCard: z.ZodObject<{
|
|
10
|
+
v: z.ZodLiteral<"messaging/1">;
|
|
11
|
+
machine: z.ZodString;
|
|
12
|
+
enc: z.ZodString;
|
|
13
|
+
person: z.ZodString;
|
|
14
|
+
label: z.ZodString;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type AdmitCard = z.infer<typeof AdmitCard>;
|
|
17
|
+
export declare function encodeTeamCard(card: TeamCard): string;
|
|
18
|
+
export declare function decodeTeamCard(text: string): TeamCard;
|
|
19
|
+
export declare function encodeAdmitCard(card: AdmitCard): string;
|
|
20
|
+
export declare function decodeAdmitCard(text: string): AdmitCard;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type ErrorCode, MessagingError } from "./schema/errors.js";
|
|
3
|
+
/** Parses with a schema, throwing a MessagingError that names the failing path but never echoes the value. */
|
|
4
|
+
export declare function parseWith<S extends z.ZodType>(schema: S, value: unknown, what: string, code?: ErrorCode): z.output<S>;
|
|
5
|
+
/**
|
|
6
|
+
* What a public entry point throws for `error`: a MessagingError unchanged; anything else (a RangeError or a
|
|
7
|
+
* TypeError caused by a hostile value) as `invalid_request`. The original is not chained as `cause`,
|
|
8
|
+
* because its message can quote the input.
|
|
9
|
+
*/
|
|
10
|
+
export declare function asMessagingError(error: unknown, what: string): MessagingError;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Identity } from "./identity.js";
|
|
2
|
+
import { type MachineId, type PersonId, type Ulid } from "./schema/common.js";
|
|
3
|
+
import { type AgentMailLevel, type AskLevel, type ChatLevel, ConsentGrant, type GrantLevels, type GrantSubject } from "./schema/consent.js";
|
|
4
|
+
import type { RealmId } from "./schema/realm.js";
|
|
5
|
+
export interface ConsentGrantArgs {
|
|
6
|
+
realm: RealmId;
|
|
7
|
+
/** The person whose policy this is: the signing device's person. */
|
|
8
|
+
person: PersonId;
|
|
9
|
+
subject: GrantSubject;
|
|
10
|
+
levels: GrantLevels;
|
|
11
|
+
now: number;
|
|
12
|
+
id?: Ulid;
|
|
13
|
+
}
|
|
14
|
+
/** Signs a new grant with `identity`, a device of `args.person`. Throws `invalid_request` for a grant that breaks a rule. */
|
|
15
|
+
export declare function signConsentGrant(identity: Identity, args: ConsentGrantArgs): ConsentGrant;
|
|
16
|
+
/**
|
|
17
|
+
* Checks a grant received from another device: the schema and its rules, the size, the realm, the person when
|
|
18
|
+
* given, and the signature by `by` over the raw value (so members a newer device adds stay covered). Whether `by`
|
|
19
|
+
* is a current device of `person` is the caller's: that changes over time (spec §6.9, "Signers"). Throws only
|
|
20
|
+
* MessagingError: `invalid_request`, `too_large` or `bad_signature`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function verifyConsentGrant(raw: unknown, ctx: {
|
|
23
|
+
realm: RealmId;
|
|
24
|
+
person?: PersonId;
|
|
25
|
+
}): ConsentGrant;
|
|
26
|
+
/** The key one grant is stored under: its subject. One grant per subject. */
|
|
27
|
+
export declare function grantSubjectKey(subject: GrantSubject): string;
|
|
28
|
+
/**
|
|
29
|
+
* Of two copies of the grant for one subject, the one that counts (spec §6.9, "Merging"): the newer by `ts`, then
|
|
30
|
+
* by `id`. Copies that tie on both are one grant signed twice: a copy whose signer is current beats one whose
|
|
31
|
+
* signer is not; between two current signers (or two removed ones), the signer that sorts first wins, then the
|
|
32
|
+
* signature. The choice is commutative and idempotent, so devices that see the same copies in any order agree.
|
|
33
|
+
*/
|
|
34
|
+
export declare function preferGrant(a: ConsentGrant, b: ConsentGrant, current: (device: MachineId) => boolean): ConsentGrant;
|
|
35
|
+
/** Levels that narrow reach: blocks never lapse (spec §6.9, "Signers"). */
|
|
36
|
+
export declare function restrictingLevels(grant: GrantLevels): GrantLevels;
|
|
37
|
+
/** Levels that widen reach: they lapse with their signer, into the person's review list. */
|
|
38
|
+
export declare function wideningLevels(grant: GrantLevels): GrantLevels;
|
|
39
|
+
/**
|
|
40
|
+
* Re-signs a grant whose signer is no longer current with `identity`, a current device of the same person
|
|
41
|
+
* (spec §6.9, "Signers"). `keep: "all"` (a replacement) keeps every level; `keep: "restricting"` (any other
|
|
42
|
+
* removal) keeps only the restricting ones, and returns undefined when there are none: the grant lapses whole.
|
|
43
|
+
* The copy keeps `id` and `ts`, so it never overrides a newer change, and records `resignedAt`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resignGrant(identity: Identity, grant: ConsentGrant, opts: {
|
|
46
|
+
now: number;
|
|
47
|
+
keep: "all" | "restricting";
|
|
48
|
+
}): ConsentGrant | undefined;
|
|
49
|
+
export declare function levelsOf(grant: GrantLevels): GrantLevels;
|
|
50
|
+
/** How the sending person relates to the receiving person (spec §6.9, "Relations"). A roster realm has only `self` and `org`. */
|
|
51
|
+
export type Relation = "self" | "org" | "contact" | "stranger";
|
|
52
|
+
/** How an item delivered to an agent may run (spec §6.9): the person's own, a trusted sender's, or a guarded peer turn. */
|
|
53
|
+
export type ConsentClass = "self" | "guarded" | "trusted";
|
|
54
|
+
/** The levels that apply to one sender, and where each came from. */
|
|
55
|
+
export interface EffectivePolicy {
|
|
56
|
+
chat: ChatLevel | "request_required";
|
|
57
|
+
agentMail: AgentMailLevel;
|
|
58
|
+
ask: AskLevel;
|
|
59
|
+
/** Set while the person grant trusts the sender. */
|
|
60
|
+
trustedUntil?: number;
|
|
61
|
+
/** Which levels a grant set, rather than the relation's default. */
|
|
62
|
+
granted: {
|
|
63
|
+
chat: boolean;
|
|
64
|
+
agentMail: boolean;
|
|
65
|
+
ask: boolean;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Spec §6.9, "Effective policy": each level is the person grant's when it sets one; otherwise, for `ask` and
|
|
70
|
+
* `agentMail`, the most permissive among the grants of channels the sender is in; otherwise the relation's
|
|
71
|
+
* default. An expired `trustedUntil` reads as absent. The caller passes only grants whose signer is current.
|
|
72
|
+
*/
|
|
73
|
+
export declare function effectivePolicy(relation: Exclude<Relation, "self">, person: GrantLevels | undefined, channels: readonly GrantLevels[], now: number): EffectivePolicy;
|
|
74
|
+
/**
|
|
75
|
+
* What arrived, classified by the receiver (spec §6.9, "Enforcement"):
|
|
76
|
+
* - `answer`: a receipt, a task update or cancel, or a message the counterpart binding ties to this person's own
|
|
77
|
+
* traffic (a reply or a follow-up); never gated;
|
|
78
|
+
* - `line`: a DM line or channel post, from the person (`agent: false`) or one of their agents (`agent: true`);
|
|
79
|
+
* - `op`: a channel change;
|
|
80
|
+
* - `ask`: mail or a task request to one of the receiver's agents, with the persons of its delegation chain (the
|
|
81
|
+
* sender's alone for mail);
|
|
82
|
+
* - `request`: a message request (class `request`), a person's first line to a person of another org.
|
|
83
|
+
*/
|
|
84
|
+
export type Inbound = {
|
|
85
|
+
kind: "answer";
|
|
86
|
+
} | {
|
|
87
|
+
kind: "line";
|
|
88
|
+
agent: boolean;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "op";
|
|
91
|
+
} | {
|
|
92
|
+
kind: "ask";
|
|
93
|
+
chain: readonly PersonId[];
|
|
94
|
+
} | {
|
|
95
|
+
kind: "request";
|
|
96
|
+
};
|
|
97
|
+
export type ConsentDecision = {
|
|
98
|
+
action: "deliver";
|
|
99
|
+
consent: ConsentClass;
|
|
100
|
+
silent?: true;
|
|
101
|
+
} | {
|
|
102
|
+
action: "hold";
|
|
103
|
+
} | {
|
|
104
|
+
action: "refuse";
|
|
105
|
+
code: "refused" | "request_required";
|
|
106
|
+
};
|
|
107
|
+
export interface DecideArgs {
|
|
108
|
+
/** The receiving person. */
|
|
109
|
+
self: PersonId;
|
|
110
|
+
/** The sending person. */
|
|
111
|
+
sender: PersonId;
|
|
112
|
+
relation: Relation;
|
|
113
|
+
/** The person grant for the sender, when its signer is current. */
|
|
114
|
+
grant?: GrantLevels;
|
|
115
|
+
/** Grants of channels the sender is in, in the receiver's own view, each with a current signer. */
|
|
116
|
+
channels: readonly GrantLevels[];
|
|
117
|
+
inbound: Inbound;
|
|
118
|
+
now: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Spec §6.9, "Enforcement": step 10 of the receiver order (§5), before the inbound policy of §8.3.
|
|
122
|
+
* - The person's own traffic is delivered as `self`.
|
|
123
|
+
* - Answers to the person's own traffic are delivered whatever the grants, so a change never strands a task.
|
|
124
|
+
* - A `blocked` chat level refuses every other class from the sender.
|
|
125
|
+
* - A `stranger` reaches the person only through a level a grant opened; otherwise `request_required`.
|
|
126
|
+
* - Lines follow `chat` (person) or `agentMail` (agent); `muted` delivers silently.
|
|
127
|
+
* - Asks follow `ask`: `open` delivers guarded, or trusted while the person grant trusts the sender and every person
|
|
128
|
+
* in the delegation chain is the sender or the receiver; `request` holds; `no` refuses, `request_required` when
|
|
129
|
+
* it is the default of a `contact` or a `stranger`.
|
|
130
|
+
* - A message request from a `stranger` is held for the person to accept; from anyone else it is a DM line.
|
|
131
|
+
*/
|
|
132
|
+
export declare function decide(args: DecideArgs): ConsentDecision;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Relation } from "./consent.js";
|
|
2
|
+
import type { Identity } from "./identity.js";
|
|
3
|
+
import { type PersonId, type Ulid } from "./schema/common.js";
|
|
4
|
+
import { type CardCan, ContactCard, type GrantLevels } from "./schema/consent.js";
|
|
5
|
+
import type { RealmId } from "./schema/realm.js";
|
|
6
|
+
export interface ContactCardArgs {
|
|
7
|
+
realm: RealmId;
|
|
8
|
+
/** The person the card speaks for: the signing device's person. */
|
|
9
|
+
from: PersonId;
|
|
10
|
+
/** The person it is for. */
|
|
11
|
+
to: PersonId;
|
|
12
|
+
seq: number;
|
|
13
|
+
now: number;
|
|
14
|
+
grantId?: Ulid;
|
|
15
|
+
can: CardCan;
|
|
16
|
+
}
|
|
17
|
+
/** Signs a card with `identity`, a device of `args.from`. Throws `invalid_request` or `too_large`. */
|
|
18
|
+
export declare function signContactCard(identity: Identity, args: ContactCardArgs): ContactCard;
|
|
19
|
+
/**
|
|
20
|
+
* Checks a card received from another person: the schema and its rules, the size, the realm, its two persons, and
|
|
21
|
+
* the signature by `by` over the raw value. Whether `by` is a current device of `from` is the caller's: a card
|
|
22
|
+
* whose signer was removed counts as absent. Throws only MessagingError: `invalid_request`, `too_large` or
|
|
23
|
+
* `bad_signature`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function verifyContactCard(raw: unknown, ctx: {
|
|
26
|
+
realm: RealmId;
|
|
27
|
+
from: PersonId;
|
|
28
|
+
to: PersonId;
|
|
29
|
+
}): ContactCard;
|
|
30
|
+
/**
|
|
31
|
+
* What a card says for a sender of `relation` with the person grant `grant` (spec §6.9, "Contact cards"): the
|
|
32
|
+
* person grant over the relation's defaults. Channel grants are not on a card: the sender knows its own channels.
|
|
33
|
+
*/
|
|
34
|
+
export declare function cardCan(relation: Exclude<Relation, "self">, grant: GrantLevels | undefined, now: number): CardCan;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { PersonId, SessionName } from "../schema/common.js";
|
|
2
|
+
import { type AgentContext, type ContextAccess, type ContextKind, type ContextRule, type ConvId, type ConvKind, type Visibility } from "../schema/conversation.js";
|
|
3
|
+
/** The defaults that apply where a person has no rule (decided 2026-11-08). `lookbackDays` absent: the retention. */
|
|
4
|
+
export declare const CONTEXT_DEFAULTS: Readonly<Record<ContextKind, {
|
|
5
|
+
access: ContextAccess;
|
|
6
|
+
lookbackDays?: number;
|
|
7
|
+
}>>;
|
|
8
|
+
/** The facts about a conversation that resolution reads. `persons` are its participant persons at the read. */
|
|
9
|
+
export interface ContextConv {
|
|
10
|
+
cid: ConvId;
|
|
11
|
+
kind: ConvKind;
|
|
12
|
+
visibility?: Visibility;
|
|
13
|
+
persons: readonly PersonId[];
|
|
14
|
+
retainDays: number;
|
|
15
|
+
agentContext: AgentContext;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The scope a conversation falls in for `person`: `own` when its participant persons are `person` alone, else its
|
|
19
|
+
* kind (a channel by its visibility). A `self` conversation has none: no agent mines it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function contextKindOf(person: PersonId, conv: Pick<ContextConv, "kind" | "visibility" | "persons">): ContextKind | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The person's rule for (agent, conversation), most specific first: a `cid` scope over a `kind` scope, then a named
|
|
24
|
+
* agent over `*`. Undefined when no rule matches; the defaults then apply.
|
|
25
|
+
*/
|
|
26
|
+
export declare function matchContextRule(rules: readonly ContextRule[], agent: SessionName, cid: ConvId, kind: ContextKind): ContextRule | undefined;
|
|
27
|
+
/** The outcome of a context read: served, held for the person (`ask`), or refused. */
|
|
28
|
+
export type ContextDecision = {
|
|
29
|
+
access: "allow" | "ask";
|
|
30
|
+
lookbackDays: number;
|
|
31
|
+
source: "rule" | "default";
|
|
32
|
+
} | {
|
|
33
|
+
access: "forbidden";
|
|
34
|
+
source: "policy" | "rule" | "self";
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The effective rule for agent `agent` of `person` reading `conv` as context (not by its seat):
|
|
38
|
+
* - the conversation's policy `seats` or `none` forbids it, whatever the person's rule says;
|
|
39
|
+
* - else the person's rule, or the default: `off` forbids, `ask` holds, `allow` serves;
|
|
40
|
+
* - the lookback is the smallest of the rule's (or the default's) `lookbackDays`, the conversation's `retainDays`,
|
|
41
|
+
* and the person's local retention for it.
|
|
42
|
+
* Throws `invalid_request` for a rule set that fails `ContextRules`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveContext(args: {
|
|
45
|
+
person: PersonId;
|
|
46
|
+
agent: SessionName;
|
|
47
|
+
conv: ContextConv;
|
|
48
|
+
rules: readonly ContextRule[];
|
|
49
|
+
localRetentionDays?: number;
|
|
50
|
+
}): ContextDecision;
|
|
51
|
+
/**
|
|
52
|
+
* What a seated agent reads by its seat (spec §16.8): everything from its seat's `seq` on, unless the policy is
|
|
53
|
+
* `none`, when it reads only the events addressed to it and the threads they are in. The person's rules do not
|
|
54
|
+
* apply to seat reads.
|
|
55
|
+
*/
|
|
56
|
+
export declare function seatReads(agentContext: AgentContext): "conversation" | "items";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Identity } from "../identity.js";
|
|
2
|
+
import { type Hash } from "../schema/common.js";
|
|
3
|
+
import { type ConvEvent, type ConvEventHeader, type ConvId, type UnsignedConvEvent } from "../schema/conversation.js";
|
|
4
|
+
/** h(0) of `cid`'s log: SHA-256(DOMAIN.convGenesis ‖ JCS(cid)), base64url. */
|
|
5
|
+
export declare function convGenesisHash(cid: ConvId): Hash;
|
|
6
|
+
/** SHA-256(JCS(event)), the raw digest, as the chain links it. Throws `invalid_request` for non-I-JSON. */
|
|
7
|
+
export declare function convEventDigest(event: unknown): Uint8Array;
|
|
8
|
+
/** h(seq) = SHA-256(h(seq − 1) ‖ SHA-256(JCS(event))), over the raw 32-byte digests, base64url. */
|
|
9
|
+
export declare function convEntryHash(prev: Hash, event: unknown): Hash;
|
|
10
|
+
/** `convEntryHash` from the event's digest: what a member checks for an entry whose content has expired. */
|
|
11
|
+
export declare function convLinkHash(prev: Hash, digest: Uint8Array): Hash;
|
|
12
|
+
/** The header of an event: everything but `body` and `sig`. JCS of it is the AAD of a sealed body. */
|
|
13
|
+
export declare function convEventHeader(event: ConvEvent | Omit<ConvEvent, "sig"> | ConvEventHeader): ConvEventHeader;
|
|
14
|
+
/**
|
|
15
|
+
* Checks that `raw` is I-JSON within `LIMITS.jsonDepth` and at most `LIMITS.convEventBytes` (`too_large`), that it
|
|
16
|
+
* matches `ConvEvent`, and that `by.device` is a usable Ed25519 key. Returns a frozen JSON copy of the raw value, so
|
|
17
|
+
* the signature and the hash cover exactly what was received. The signature is not checked.
|
|
18
|
+
*/
|
|
19
|
+
export declare function admitConvEvent(raw: unknown): ConvEvent;
|
|
20
|
+
/**
|
|
21
|
+
* Signs an event (spec §16.4): `sig = Ed25519(by.device, DOMAIN.convEvent ‖ JCS(event without sig))`. `by.device`
|
|
22
|
+
* must be `identity`'s machine. Refuses an event that fails the schema (`invalid_request`) or is over
|
|
23
|
+
* `LIMITS.convEventBytes` once signed (`too_large`). Authority is checked when the event is applied.
|
|
24
|
+
*/
|
|
25
|
+
export declare function signConvEvent<E extends UnsignedConvEvent>(identity: Identity, unsigned: E): E & {
|
|
26
|
+
sig: string;
|
|
27
|
+
};
|
|
28
|
+
/** True only for a valid signature by `event.by.device` over the event without `sig`. Never throws. */
|
|
29
|
+
export declare function convEventSigned(event: ConvEvent): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Admits `raw` (`admitConvEvent`) and checks its signature over the raw value (`bad_signature`). Membership and
|
|
32
|
+
* authority are `ConvLog.apply`'s. Throws only MessagingError.
|
|
33
|
+
*/
|
|
34
|
+
export declare function verifyConvEvent(raw: unknown): ConvEvent;
|
|
35
|
+
/** Freezes a JSON value and everything in it, iteratively. */
|
|
36
|
+
export declare function deepFreeze<T>(value: T): T;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PersonId } from "../schema/common.js";
|
|
2
|
+
import { type ConvId, ParticipantId } from "../schema/conversation.js";
|
|
3
|
+
/**
|
|
4
|
+
* The id of the dm between `a` and `b`: `"d_" + base32lower(SHA-256(DOMAIN.dm ‖ JCS([a, b] sorted)))[0..26]`. Both
|
|
5
|
+
* ends derive the same id, so a dm has no create race. `a` may equal `b`: a person's note to themselves.
|
|
6
|
+
*/
|
|
7
|
+
export declare function dmConvId(a: ParticipantId, b: ParticipantId): ConvId;
|
|
8
|
+
/** The id of `person`'s own `self` conversation: `"s_" + base32lower(SHA-256(DOMAIN.self ‖ JCS(person)))[0..26]`. */
|
|
9
|
+
export declare function selfConvId(person: PersonId): ConvId;
|
|
10
|
+
/** The person a participant is, or belongs to. */
|
|
11
|
+
export declare function participantPerson(p: ParticipantId): PersonId;
|
|
12
|
+
/** The agent's session name, for an agent participant. */
|
|
13
|
+
export declare function participantAgent(p: ParticipantId): string | undefined;
|
|
14
|
+
/** The participant an event's `by` names: `person`, or `person/agent`. */
|
|
15
|
+
export declare function participantOf(by: {
|
|
16
|
+
person: PersonId;
|
|
17
|
+
agent?: string | undefined;
|
|
18
|
+
}): ParticipantId;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MachineId, PersonId } from "../schema/common.js";
|
|
2
|
+
import { type LinkRecord, type MsgBody, type ReaderId } from "../schema/conversation.js";
|
|
3
|
+
/** The links in force: for each link id the record written last (records are in `seq` order), unless revoked. */
|
|
4
|
+
export declare function currentLinks(records: readonly LinkRecord[]): LinkRecord[];
|
|
5
|
+
/**
|
|
6
|
+
* Checks a line's `via` against the author's links, as the author's own devices can (other members cannot see the
|
|
7
|
+
* person's links): only a person writes from a linked channel, never an agent, and only through a link of that
|
|
8
|
+
* channel that the authoring device hosts and that is in force. Throws `forbidden`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkVia(body: MsgBody, by: {
|
|
11
|
+
person: PersonId;
|
|
12
|
+
device: MachineId;
|
|
13
|
+
agent?: string;
|
|
14
|
+
}, links: readonly LinkRecord[]): void;
|
|
15
|
+
/** The reader id of a link's channel provider, for the audience guard: `ext:<channel>:<link id>`. */
|
|
16
|
+
export declare function linkReader(link: Pick<LinkRecord, "channel" | "id">): ReaderId;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { AnchorRelation } from "../realm.js";
|
|
2
|
+
import { type Hash, type MachineId, type PersonId } from "../schema/common.js";
|
|
3
|
+
import { type AgentContext, type BridgePolicy, type ConvEvent, type ConvHead, type ConvId, type ConvKind, type ConvLogEntry, type ConvRole, type HistoryPolicy, type MsgKind, type ParticipantId, type Permit, type RekeyCause, type RespondTo, type Visibility } from "../schema/conversation.js";
|
|
4
|
+
import type { RealmId } from "../schema/realm.js";
|
|
5
|
+
/** A participant at one position. */
|
|
6
|
+
export interface ConvParticipant {
|
|
7
|
+
p: ParticipantId;
|
|
8
|
+
person: PersonId;
|
|
9
|
+
/** The agent's session name, for an agent participant. */
|
|
10
|
+
agent?: string;
|
|
11
|
+
role: ConvRole;
|
|
12
|
+
/** `invited` until the person accepts, or until the agent's person seats it. */
|
|
13
|
+
state: "member" | "invited";
|
|
14
|
+
device?: MachineId;
|
|
15
|
+
respondTo?: RespondTo;
|
|
16
|
+
/** The seq at which it became a participant. */
|
|
17
|
+
since: number;
|
|
18
|
+
}
|
|
19
|
+
/** Everything the log decides at one position. Frozen. */
|
|
20
|
+
export interface ConvSnapshot {
|
|
21
|
+
cid: ConvId;
|
|
22
|
+
kind: ConvKind;
|
|
23
|
+
visibility?: Visibility;
|
|
24
|
+
history: HistoryPolicy;
|
|
25
|
+
retainDays: number;
|
|
26
|
+
org?: string;
|
|
27
|
+
post: Permit;
|
|
28
|
+
invite: Permit;
|
|
29
|
+
pins: Permit;
|
|
30
|
+
archived: boolean;
|
|
31
|
+
epoch: number;
|
|
32
|
+
/** The seq and `ts` of the op that started the epoch. */
|
|
33
|
+
epochSince: number;
|
|
34
|
+
epochTs: number;
|
|
35
|
+
/** Causes that need a `rekey` before the next message (spec §16.7). Sorted. */
|
|
36
|
+
rekeyDue: RekeyCause[];
|
|
37
|
+
/** Sorted by `p`. */
|
|
38
|
+
participants: ConvParticipant[];
|
|
39
|
+
/** The effective policy. In a channel the owners' and admins'; in a dm or a group the strictest vote. */
|
|
40
|
+
agentContext: AgentContext;
|
|
41
|
+
/**
|
|
42
|
+
* Who may mirror the conversation to a linked channel (reserved: no daemon acts on it yet). In a channel the
|
|
43
|
+
* owners' and admins'; in a dm or a group the strictest vote, where a person who has not voted counts as `none`.
|
|
44
|
+
*/
|
|
45
|
+
bridges: BridgePolicy;
|
|
46
|
+
/** dm and group: each participant person's votes, sorted by person. */
|
|
47
|
+
votes: ConvVote[];
|
|
48
|
+
/** The seq of the entry that last changed any of the above. */
|
|
49
|
+
changed: number;
|
|
50
|
+
}
|
|
51
|
+
/** One participant person's policy votes in a dm or group. */
|
|
52
|
+
export interface ConvVote {
|
|
53
|
+
person: PersonId;
|
|
54
|
+
agentContext?: AgentContext;
|
|
55
|
+
bridges?: BridgePolicy;
|
|
56
|
+
}
|
|
57
|
+
/** What a member knows of one event, by id. */
|
|
58
|
+
export interface ConvMessage {
|
|
59
|
+
id: string;
|
|
60
|
+
seq: number;
|
|
61
|
+
class: ConvEvent["class"];
|
|
62
|
+
author: ParticipantId;
|
|
63
|
+
/** Known when the body was opened. */
|
|
64
|
+
t?: MsgKind;
|
|
65
|
+
deleted: boolean;
|
|
66
|
+
pinned: boolean;
|
|
67
|
+
reactions: {
|
|
68
|
+
key: string;
|
|
69
|
+
by: ParticipantId[];
|
|
70
|
+
}[];
|
|
71
|
+
/** For a task request: its executor. */
|
|
72
|
+
taskTo?: ParticipantId;
|
|
73
|
+
/** For a task request: true. */
|
|
74
|
+
taskRoot?: boolean;
|
|
75
|
+
}
|
|
76
|
+
/** Checks only a live append can make: they depend on the realm now, not on the log. */
|
|
77
|
+
export interface ConvLiveChecks {
|
|
78
|
+
/** Wall-clock time; the event's `ts` must be within `skewMs` (default `LIMITS.clockSkewMs`) of it (`expired`). */
|
|
79
|
+
now?: number;
|
|
80
|
+
skewMs?: number;
|
|
81
|
+
/** True when `device` is a current device of `person` in the realm (`unknown_device` otherwise). */
|
|
82
|
+
isDevice?: (person: PersonId, device: MachineId) => boolean;
|
|
83
|
+
/** The org of `person`, for `join`, and for an add of a person who is not a guest (`forbidden`). */
|
|
84
|
+
orgOf?: (person: PersonId) => string | undefined;
|
|
85
|
+
}
|
|
86
|
+
export interface ConvApplyOptions {
|
|
87
|
+
/** The opened body of a `msg` entry; the message rules run only with it. */
|
|
88
|
+
body?: unknown;
|
|
89
|
+
live?: ConvLiveChecks;
|
|
90
|
+
}
|
|
91
|
+
/** An entry whose content the relay expired: its seq, its prev and the digest SHA-256(JCS(event)). */
|
|
92
|
+
export interface ConvExpiredEntry {
|
|
93
|
+
seq: number;
|
|
94
|
+
prev: Hash;
|
|
95
|
+
digest: Hash;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A verified conversation log. Every method throws only MessagingError. Entries are kept as frozen copies of the
|
|
99
|
+
* received JSON, so hashes cover exactly what was received.
|
|
100
|
+
*/
|
|
101
|
+
export declare class ConvLog {
|
|
102
|
+
#private;
|
|
103
|
+
readonly cid: ConvId;
|
|
104
|
+
readonly realm: RealmId;
|
|
105
|
+
private constructor();
|
|
106
|
+
/** An empty log of `cid` in `realm`: its head is {0, h(0)}. */
|
|
107
|
+
static open(cid: ConvId, realm: RealmId): ConvLog;
|
|
108
|
+
/** Replays `entries` (each with the opened body of a `msg`, when the caller has it). Throws the first failure. */
|
|
109
|
+
static fromEntries(cid: ConvId, realm: RealmId, entries: {
|
|
110
|
+
entry: unknown;
|
|
111
|
+
body?: unknown;
|
|
112
|
+
}[]): ConvLog;
|
|
113
|
+
head(): ConvHead;
|
|
114
|
+
/** The last op's seq and hash, or {0, h(0)}: what an author puts in `opHead`. */
|
|
115
|
+
opHead(): ConvHead;
|
|
116
|
+
/** h(seq), for 0 ≤ seq ≤ head. */
|
|
117
|
+
hashAt(seq: number): Hash | undefined;
|
|
118
|
+
entries(): (ConvLogEntry | ConvExpiredEntry)[];
|
|
119
|
+
/** The state at the head; undefined before `create`. */
|
|
120
|
+
state(): ConvSnapshot | undefined;
|
|
121
|
+
/** The state after the entry at `seq`, as a log holding only entries 1..seq has it. */
|
|
122
|
+
at(seq: number): ConvSnapshot | undefined;
|
|
123
|
+
/** What a member knows of the event `id`. */
|
|
124
|
+
message(id: string): ConvMessage | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Gossip (spec §16.4): `ahead` past the head (fetch up to it first); `consistent` when h(seq) is ours; `diverged`
|
|
127
|
+
* when this log has another hash at that seq, a fork whose evidence is the signed event that named it.
|
|
128
|
+
*/
|
|
129
|
+
relate(head: ConvHead): AnchorRelation;
|
|
130
|
+
/**
|
|
131
|
+
* Checks one entry and appends it, in order: I-JSON and size (`invalid_request`, `too_large`); schema, realm and
|
|
132
|
+
* cid (`invalid_request`); the chain, seq = head + 1 and prev = h(head) (`conv_conflict`); the signature
|
|
133
|
+
* (`bad_signature`); the live checks given (`expired`, `unknown_device`); a reused id (`duplicate`); then the
|
|
134
|
+
* authority rules of spec §16.7. Returns the entry's h(seq).
|
|
135
|
+
*/
|
|
136
|
+
apply(raw: unknown, options?: ConvApplyOptions): Hash;
|
|
137
|
+
/**
|
|
138
|
+
* Appends an entry whose content the relay expired (spec §16.4, "Retention"), by its digest: the chain stays
|
|
139
|
+
* verifiable and the state does not change. An op is never expired: a later op head that names this seq fails.
|
|
140
|
+
*/
|
|
141
|
+
applyExpired(raw: ConvExpiredEntry): Hash;
|
|
142
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type MachineId } from "../schema/common.js";
|
|
3
|
+
import { type KeyShare } from "../schema/conversation.js";
|
|
4
|
+
/** One ratchet step: the message key for this index and the chain key for the next. */
|
|
5
|
+
export declare function chainStep(ck: Uint8Array): {
|
|
6
|
+
mk: Uint8Array;
|
|
7
|
+
next: Uint8Array;
|
|
8
|
+
};
|
|
9
|
+
/** A sender chain as a device stores it. */
|
|
10
|
+
export declare const SenderChainState: z.ZodObject<{
|
|
11
|
+
epoch: z.ZodNumber;
|
|
12
|
+
device: z.ZodString;
|
|
13
|
+
start: z.ZodNumber;
|
|
14
|
+
origin: z.ZodString;
|
|
15
|
+
idx: z.ZodNumber;
|
|
16
|
+
ck: z.ZodString;
|
|
17
|
+
startedAt: z.ZodNumber;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type SenderChainState = z.infer<typeof SenderChainState>;
|
|
20
|
+
/** The sending side of one chain. `next()` hands out each message key once. */
|
|
21
|
+
export declare class SenderChain {
|
|
22
|
+
#private;
|
|
23
|
+
readonly epoch: number;
|
|
24
|
+
readonly device: MachineId;
|
|
25
|
+
/** The index this chain starts at. */
|
|
26
|
+
readonly start: number;
|
|
27
|
+
readonly startedAt: number;
|
|
28
|
+
private constructor();
|
|
29
|
+
/** A new chain at `idx` (default 0) of `epoch`, from a fresh random chain key. */
|
|
30
|
+
static start(args: {
|
|
31
|
+
epoch: number;
|
|
32
|
+
device: MachineId;
|
|
33
|
+
idx?: number;
|
|
34
|
+
now: number;
|
|
35
|
+
}): SenderChain;
|
|
36
|
+
/** `start` with a caller-chosen chain key: for test vectors, and for a device restoring its own state. */
|
|
37
|
+
static startWith(args: {
|
|
38
|
+
epoch: number;
|
|
39
|
+
device: MachineId;
|
|
40
|
+
idx?: number;
|
|
41
|
+
now: number;
|
|
42
|
+
}, ck: Uint8Array): SenderChain;
|
|
43
|
+
static restore(raw: unknown): SenderChain;
|
|
44
|
+
export(): SenderChainState;
|
|
45
|
+
/** The index the next message gets. */
|
|
46
|
+
get idx(): number;
|
|
47
|
+
/** The share that lets a receiver open every message of this chain: its chain key at its start. */
|
|
48
|
+
share(): KeyShare;
|
|
49
|
+
/** The key for the next message, and its index. The chain moves past it. */
|
|
50
|
+
next(): {
|
|
51
|
+
idx: number;
|
|
52
|
+
mk: Uint8Array;
|
|
53
|
+
};
|
|
54
|
+
/** True once the chain has sent `LIMITS.chainMessages` messages or is `LIMITS.chainMaxMs` old: rotate before the next. */
|
|
55
|
+
due(now: number): boolean;
|
|
56
|
+
/** A fresh chain in the same epoch, starting at this chain's next index. Share it before sending on it. */
|
|
57
|
+
rotate(now: number): SenderChain;
|
|
58
|
+
/** `rotate` with a caller-chosen chain key, for test vectors. */
|
|
59
|
+
rotateWith(now: number, ck: Uint8Array): SenderChain;
|
|
60
|
+
}
|
|
61
|
+
export declare const ReceiverChainsState: z.ZodObject<{
|
|
62
|
+
chains: z.ZodArray<z.ZodObject<{
|
|
63
|
+
epoch: z.ZodNumber;
|
|
64
|
+
device: z.ZodString;
|
|
65
|
+
start: z.ZodNumber;
|
|
66
|
+
origin: z.ZodString;
|
|
67
|
+
pos: z.ZodNumber;
|
|
68
|
+
ck: z.ZodString;
|
|
69
|
+
skipped: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodString], null>>;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
}, z.core.$strict>;
|
|
72
|
+
export type ReceiverChainsState = z.infer<typeof ReceiverChainsState>;
|
|
73
|
+
/**
|
|
74
|
+
* The receiving side: every chain this device holds for one conversation, by (epoch, sender device). It derives
|
|
75
|
+
* forward only and hands out each message key once; a message that arrives out of order uses a key kept from an
|
|
76
|
+
* earlier derivation, at most `LIMITS.skippedKeys` per sender and epoch. The chain keys at each share's index are
|
|
77
|
+
* kept, so the device can share them with a late joiner (history `shared`).
|
|
78
|
+
*/
|
|
79
|
+
export declare class ReceiverChains {
|
|
80
|
+
#private;
|
|
81
|
+
static restore(raw: unknown): ReceiverChains;
|
|
82
|
+
export(): ReceiverChainsState;
|
|
83
|
+
/**
|
|
84
|
+
* Adds a share. The first share at a given (epoch, device, idx) wins: a later one with another key changes
|
|
85
|
+
* nothing, and `add` returns false. Returns true when the share was new.
|
|
86
|
+
*/
|
|
87
|
+
add(raw: KeyShare): boolean;
|
|
88
|
+
/** Every share this device holds, from each chain's start: what it shares with a late joiner. */
|
|
89
|
+
shares(): KeyShare[];
|
|
90
|
+
/**
|
|
91
|
+
* The message key for (`epoch`, `device`, `idx`), used once. `not_found` when no share covers it, or its key was
|
|
92
|
+
* already used or dropped; `too_large` when it is more than `LIMITS.skippedKeys` past the chain's position.
|
|
93
|
+
*/
|
|
94
|
+
messageKey(epoch: number, device: MachineId, idx: number): Uint8Array;
|
|
95
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Identity } from "../identity.js";
|
|
2
|
+
import { type EncKey, type MachineId, type PersonId } from "../schema/common.js";
|
|
3
|
+
import { type ConvKeysEvent, ConvMeta, type ConvMsgEvent, type ConvOpEvent, type KeyShare, MsgBody } from "../schema/conversation.js";
|
|
4
|
+
/** A message event before its body: the header its author signs, with `class: "msg"`. */
|
|
5
|
+
export type UnsealedMsg = Omit<ConvMsgEvent, "body" | "sig">;
|
|
6
|
+
/** A keys event before its body. */
|
|
7
|
+
export type UnsealedKeys = Omit<ConvKeysEvent, "body" | "sig">;
|
|
8
|
+
/** A recipient device of a key share: its person, its machine and its X25519 key, as the realm verified them. */
|
|
9
|
+
export interface ShareRecipient {
|
|
10
|
+
person: PersonId;
|
|
11
|
+
device: MachineId;
|
|
12
|
+
enc: EncKey;
|
|
13
|
+
}
|
|
14
|
+
/** Fixed randomness of one sealed share, for test vectors: the ephemeral X25519 private key and the nonce. */
|
|
15
|
+
export interface ShareRandomness {
|
|
16
|
+
ephemeral: Uint8Array;
|
|
17
|
+
nonce: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
/** Seals `body` under `mk` as the body of `unsealed` and signs the event. Throws only MessagingError. */
|
|
20
|
+
export declare function sealConvMsg(identity: Identity, unsealed: UnsealedMsg, body: MsgBody, mk: Uint8Array): ConvMsgEvent;
|
|
21
|
+
/** `sealConvMsg` with a caller-chosen nonce: for test vectors only (`@messaging/core/testing`). */
|
|
22
|
+
export declare function sealConvMsgWith(identity: Identity, unsealed: UnsealedMsg, body: MsgBody, mk: Uint8Array, nonce: Uint8Array): ConvMsgEvent;
|
|
23
|
+
/**
|
|
24
|
+
* Opens a `msg` event's body with its message key: `bad_signature` when it fails to open, `too_large` over
|
|
25
|
+
* `LIMITS.convPlaintextBytes`, `invalid_request` when the plaintext is not a `MsgBody` (parsed with `parseWire`).
|
|
26
|
+
* The caller has verified the event's signature.
|
|
27
|
+
*/
|
|
28
|
+
export declare function openConvMsg(event: ConvMsgEvent, mk: Uint8Array): MsgBody;
|
|
29
|
+
/** `create.sealedMeta`: JCS(meta) sealed under the creator's epoch-0 key 0, with the create event's header as AAD. */
|
|
30
|
+
export declare function sealConvMeta(header: Omit<ConvOpEvent, "body" | "sig">, meta: ConvMeta, mk0: Uint8Array): string;
|
|
31
|
+
/** `sealConvMeta` with a caller-chosen nonce: for test vectors only. */
|
|
32
|
+
export declare function sealConvMetaWith(header: Omit<ConvOpEvent, "body" | "sig">, meta: ConvMeta, mk0: Uint8Array, nonce: Uint8Array): string;
|
|
33
|
+
/** Opens a `create` event's `sealedMeta`. Errors as for `openConvMsg`; `not_found` when it has none. */
|
|
34
|
+
export declare function openConvMeta(event: ConvOpEvent, mk0: Uint8Array): ConvMeta;
|
|
35
|
+
/**
|
|
36
|
+
* Seals `shares` to each recipient device as the body of `unsealed`, and signs the event. One sealed share per
|
|
37
|
+
* device; at most `LIMITS.keySharesPerEvent` devices and `LIMITS.keyShares` chain keys. Throws only MessagingError.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sealKeyShares(identity: Identity, unsealed: UnsealedKeys, recipients: ShareRecipient[], shares: KeyShare[]): ConvKeysEvent;
|
|
40
|
+
/** `sealKeyShares` with fixed randomness per recipient, in order: for test vectors only. */
|
|
41
|
+
export declare function sealKeySharesWith(identity: Identity, unsealed: UnsealedKeys, recipients: ShareRecipient[], shares: KeyShare[], randomness: ShareRandomness[]): ConvKeysEvent;
|
|
42
|
+
/**
|
|
43
|
+
* Opens the share sealed to `identity`'s device in a `keys` event: `not_found` when there is none, `bad_signature`
|
|
44
|
+
* when it fails to open, `invalid_request` when the plaintext is not a share payload. The caller has verified the
|
|
45
|
+
* event's signature and applied it to the log.
|
|
46
|
+
*/
|
|
47
|
+
export declare function openKeyShares(event: ConvKeysEvent, identity: Identity): KeyShare[];
|