@joe-broadhead/messaging 0.0.0-stage → 0.7.0
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 +175 -0
- package/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +36 -2
- package/dist/bin/messaging-mcp.js +25887 -0
- package/dist/bin/messaging-relay.js +19014 -0
- package/dist/bin/messaging.js +14732 -0
- package/dist/bin/messagingd.js +28682 -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 +458 -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/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 +44 -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 +254 -0
- package/dist/lib/modules/core/schema/consent.js +120 -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 +65 -0
- package/dist/lib/modules/core/schema/index.js +15 -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 +20 -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 +3426 -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 +948 -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/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 +38 -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 +272 -0
- package/dist/types/core/schema/consent.d.ts +140 -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 +750 -0
- package/dist/types/core/schema/errors.d.ts +104 -0
- package/dist/types/core/schema/index.d.ts +15 -0
- package/dist/types/core/schema/ipc/consent.d.ts +555 -0
- package/dist/types/core/schema/ipc/device.d.ts +729 -0
- package/dist/types/core/schema/ipc/jelly.d.ts +501 -0
- package/dist/types/core/schema/ipc/shapes.d.ts +411 -0
- package/dist/types/core/schema/ipc/surfaces.d.ts +674 -0
- package/dist/types/core/schema/ipc.d.ts +3131 -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 +1122 -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 +18 -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,71 @@
|
|
|
1
|
+
import type { Identity } from "./identity.js";
|
|
2
|
+
import type { RealmView } from "./realm.js";
|
|
3
|
+
import { type Hash, type MachineId, type SessionAddress, type Ulid } from "./schema/common.js";
|
|
4
|
+
import type { RealmId } from "./schema/realm.js";
|
|
5
|
+
import { DelegationGrant, type DelegationSummary } from "./schema/delegation.js";
|
|
6
|
+
/** SHA-256 of the JCS form of the full grant, including `sig`: the next grant's `parent`. */
|
|
7
|
+
export declare function grantHash(grant: DelegationGrant): Hash;
|
|
8
|
+
export interface GrantArgs {
|
|
9
|
+
realm: RealmId;
|
|
10
|
+
task: Ulid;
|
|
11
|
+
/** The delegating session, on the signing machine. */
|
|
12
|
+
from: SessionAddress;
|
|
13
|
+
to: SessionAddress;
|
|
14
|
+
/** The grant this one extends: the last grant of the chain. Absent for a root. */
|
|
15
|
+
parent?: DelegationGrant;
|
|
16
|
+
/** Default: the parent's scope, or unrestricted for a root. Never wider than the parent's (`forbidden`). */
|
|
17
|
+
scope?: readonly string[];
|
|
18
|
+
/** Default: the parent's maxDepth, or `LIMITS.delegationDepth` for a root. Never above the parent's (`forbidden`). */
|
|
19
|
+
maxDepth?: number;
|
|
20
|
+
/** Default and maximum `LIMITS.ttlMs`. A grant never outlives its parent. */
|
|
21
|
+
ttlMs?: number;
|
|
22
|
+
now?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Signs one grant with the machine key (spec §6.7): a root, or the next hop after `parent`. The new grant
|
|
26
|
+
* may narrow the parent's scope and maxDepth, never widen them, and expires no later than the parent.
|
|
27
|
+
* It does not check the chain as a whole; run `verifyChain` on the extended chain for that.
|
|
28
|
+
* Throws only MessagingError: `invalid_request` for bad arguments, `forbidden` for a widening, `expired`
|
|
29
|
+
* for a parent that has expired.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createGrant(identity: Identity, args: GrantArgs): DelegationGrant;
|
|
32
|
+
/** The hop a chain arrived on: the envelope that carried it and the task it requests. */
|
|
33
|
+
export interface ChainHop {
|
|
34
|
+
/** The envelope's `from.device`. */
|
|
35
|
+
by: MachineId;
|
|
36
|
+
/** The sender's address, `person/session@label`. */
|
|
37
|
+
from: SessionAddress;
|
|
38
|
+
/** The addressed session on this machine. */
|
|
39
|
+
to: SessionAddress;
|
|
40
|
+
/** The task id of the request. */
|
|
41
|
+
task: Ulid;
|
|
42
|
+
}
|
|
43
|
+
export interface ChainContext {
|
|
44
|
+
/** The locally verified realm: a `Roster` for a roster realm. */
|
|
45
|
+
realm: RealmView;
|
|
46
|
+
now: number;
|
|
47
|
+
hop: ChainHop;
|
|
48
|
+
}
|
|
49
|
+
export interface VerifiedChain {
|
|
50
|
+
/** The raw grants, unknown fields included: store them to extend the chain later. */
|
|
51
|
+
grants: DelegationGrant[];
|
|
52
|
+
summary: DelegationSummary;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Verifies a delegation chain for the hop it arrived on (spec §6.7), against the locally verified realm.
|
|
56
|
+
* Checks, in order, stopping at the first failure:
|
|
57
|
+
*
|
|
58
|
+
* 1. schema, I-JSON and 1…`LIMITS.delegationDepth` grants (`invalid_request`); JCS within
|
|
59
|
+
* `LIMITS.delegationBytes` (`too_large`);
|
|
60
|
+
* 2. for each grant, root first: this realm (`invalid_request`); the time window, as for envelopes
|
|
61
|
+
* (`expired`); `by` a current member whose person and label `from` names (`not_member`); the
|
|
62
|
+
* signature over the raw grant (`bad_signature`); linkage, no `parent` on the root and `from = previous.to`,
|
|
63
|
+
* `parent = grantHash(previous)` after it (`invalid_request`); narrowing of scope, maxDepth and exp
|
|
64
|
+
* (`forbidden`);
|
|
65
|
+
* 3. at most the smallest maxDepth grants (`forbidden`);
|
|
66
|
+
* 4. no session twice (`forbidden`);
|
|
67
|
+
* 5. the last grant is for this hop (`forbidden`).
|
|
68
|
+
*
|
|
69
|
+
* Throws only MessagingError, and never quotes the chain.
|
|
70
|
+
*/
|
|
71
|
+
export declare function verifyChain(raw: unknown, ctx: ChainContext): VerifiedChain;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A hosted person's device log (spec §15.3): an append-only, hash-chained list of signed entries that binds,
|
|
3
|
+
* attests and unbinds the person's devices. `DeviceLog` replays and extends it, enforcing every authority rule;
|
|
4
|
+
* the directory and every client run the same checks. Binding freshness against the epoch log (an entry's
|
|
5
|
+
* first epoch) is `verifyPersonView`'s, which knows the epochs.
|
|
6
|
+
*/
|
|
7
|
+
import type { KeyObject } from "node:crypto";
|
|
8
|
+
import { Identity } from "../identity.js";
|
|
9
|
+
import { type Hash, type HostedPersonId } from "../schema/common.js";
|
|
10
|
+
import { LogEntry, type DeviceId, type DeviceKind, type UnbindReason } from "../schema/directory.js";
|
|
11
|
+
import type { RealmId } from "../schema/realm.js";
|
|
12
|
+
import { type JellyKey, type JellyToken } from "./jelly-token.js";
|
|
13
|
+
/** How strongly a device is vouched for: by Jelly ID, or by approval from a Jelly-vouched device. */
|
|
14
|
+
export type Vouch = "jelly" | "device";
|
|
15
|
+
/** A device as its person's log has it. */
|
|
16
|
+
export interface DeviceRecord {
|
|
17
|
+
readonly device: DeviceId;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly kind: DeviceKind;
|
|
20
|
+
readonly vouch: Vouch;
|
|
21
|
+
/** The `ts` of its `bind`. */
|
|
22
|
+
readonly since: number;
|
|
23
|
+
/** The approver of a device bound by link. */
|
|
24
|
+
readonly approver?: DeviceId | undefined;
|
|
25
|
+
/** The Jelly host id of its latest Jelly token, for a Jelly-vouched device. */
|
|
26
|
+
readonly host?: string | undefined;
|
|
27
|
+
/** False once unbound. */
|
|
28
|
+
readonly current: boolean;
|
|
29
|
+
readonly unbound?: {
|
|
30
|
+
readonly ts: number;
|
|
31
|
+
readonly reason: UnbindReason;
|
|
32
|
+
readonly by: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
}
|
|
35
|
+
/** The person's profile, from the newest Jelly token of the log (spec §15.3): Jelly-signed claims. */
|
|
36
|
+
export interface JellyProfile {
|
|
37
|
+
readonly iss: string;
|
|
38
|
+
readonly sub: string;
|
|
39
|
+
readonly name?: string | undefined;
|
|
40
|
+
/** Only when Jelly ID proved it (`email_verified`). */
|
|
41
|
+
readonly email?: string | undefined;
|
|
42
|
+
/** The `ts` of the entry that carried it. */
|
|
43
|
+
readonly at: number;
|
|
44
|
+
}
|
|
45
|
+
export interface DeviceLogContext {
|
|
46
|
+
readonly realm: RealmId;
|
|
47
|
+
readonly person: HostedPersonId;
|
|
48
|
+
/** The realm's Jelly issuer and resource, and the keys `JellyKeyring.prepare` found for the log's tokens. */
|
|
49
|
+
readonly jelly: {
|
|
50
|
+
readonly issuer: string;
|
|
51
|
+
readonly audience: string;
|
|
52
|
+
readonly key: (kid: string) => JellyKey | undefined;
|
|
53
|
+
};
|
|
54
|
+
/** The directory's epoch keys, current and past: the only signers of a `service` unbind. */
|
|
55
|
+
readonly directoryKeys: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
export interface AppendOptions {
|
|
58
|
+
/** The directory's clock when it accepts an append: `ts` must be within the clock skew of it (`expired`). */
|
|
59
|
+
readonly now?: number | undefined;
|
|
60
|
+
}
|
|
61
|
+
/** `SHA-256(JCS(entry))`, the signature included. */
|
|
62
|
+
export declare function logEntryHash(entry: LogEntry): Hash;
|
|
63
|
+
/** A log entry without its signer and signature, as a caller builds it. */
|
|
64
|
+
export type UnsignedLogEntry = Omit<LogEntry, "by" | "sig">;
|
|
65
|
+
/**
|
|
66
|
+
* Signs an entry: `by` becomes the signer's public key, `sig` its signature over JCS(entry without sig). A
|
|
67
|
+
* device signs with its `Identity`; the directory signs the unbinds of an erased account with its epoch key.
|
|
68
|
+
* Throws `invalid_request`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function signLogEntry(signer: Identity | KeyObject, entry: UnsignedLogEntry): LogEntry;
|
|
71
|
+
/**
|
|
72
|
+
* One person's device log, verified entry by entry from `seq` 0. A failed `append` changes nothing. Errors:
|
|
73
|
+
* `invalid_request` (schema, size), `log_conflict` (not on the head), `device_limit` (an eleventh device),
|
|
74
|
+
* `expired` (with `now`), and `binding_invalid` for every other rule and signature.
|
|
75
|
+
*/
|
|
76
|
+
export declare class DeviceLog {
|
|
77
|
+
#private;
|
|
78
|
+
readonly realm: RealmId;
|
|
79
|
+
readonly person: HostedPersonId;
|
|
80
|
+
constructor(realm: RealmId, person: HostedPersonId);
|
|
81
|
+
/** A copy that appends independently of this log. Entries are not re-verified. */
|
|
82
|
+
clone(): DeviceLog;
|
|
83
|
+
/** Replays `entries` from `seq` 0. */
|
|
84
|
+
static fromEntries(entries: readonly unknown[], context: DeviceLogContext): DeviceLog;
|
|
85
|
+
get length(): number;
|
|
86
|
+
/** The head `{seq, hash}`, or undefined for an empty log. */
|
|
87
|
+
head(): {
|
|
88
|
+
seq: number;
|
|
89
|
+
hash: Hash;
|
|
90
|
+
} | undefined;
|
|
91
|
+
/** The hash of entry `seq`. */
|
|
92
|
+
hashAt(seq: number): Hash | undefined;
|
|
93
|
+
entries(): readonly LogEntry[];
|
|
94
|
+
/** Every device the log has bound, current or not. */
|
|
95
|
+
allDevices(): DeviceRecord[];
|
|
96
|
+
/** The current devices. */
|
|
97
|
+
devices(): DeviceRecord[];
|
|
98
|
+
device(device: DeviceId): DeviceRecord | undefined;
|
|
99
|
+
/** The device that replaced `device` (an `unbind` with reason `replaced`), if it did and is current (spec §15.3). */
|
|
100
|
+
successor(device: DeviceId): DeviceId | undefined;
|
|
101
|
+
/** The profile from the newest Jelly token in the log. */
|
|
102
|
+
profile(): JellyProfile | undefined;
|
|
103
|
+
/** The verified Jelly token of entry `seq`, for an entry with a `jelly` authority. */
|
|
104
|
+
token(seq: number): JellyToken | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* A `bind` or `attest` of this log carried a sign-in's token (`src: "device"`) with this `jti`: that token
|
|
107
|
+
* vouched for its one act and vouches for nothing else (spec §15.3), an erasure included.
|
|
108
|
+
*/
|
|
109
|
+
spentSignIn(jti: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* The latest time at which entry `seq` may first appear in an epoch: its Jelly token's `exp` plus the clock
|
|
112
|
+
* skew, in ms. Undefined for an entry with no `jelly` authority.
|
|
113
|
+
*/
|
|
114
|
+
bindingDeadline(seq: number): number | undefined;
|
|
115
|
+
/** Checks `entry` as the next entry and appends it. */
|
|
116
|
+
append(entry: unknown, context: DeviceLogContext, options?: AppendOptions): LogEntry;
|
|
117
|
+
/** Checks `entry` as the next entry without appending it. */
|
|
118
|
+
check(entry: unknown, context: DeviceLogContext, options?: AppendOptions): LogEntry;
|
|
119
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The epoch log's signed tree heads (spec §15.6): signing and verifying STHs and epoch-key rotations, proving
|
|
3
|
+
* an epoch leaf into an STH, and the checks a client makes before it adopts a newer STH (§15.6, "Adopting").
|
|
4
|
+
*/
|
|
5
|
+
import type { KeyObject } from "node:crypto";
|
|
6
|
+
import { type Hash } from "../schema/common.js";
|
|
7
|
+
import { EpochLeaf, EpochProof, KeyRotation, STH } from "../schema/directory.js";
|
|
8
|
+
import type { EpochAnchor, RealmId } from "../schema/realm.js";
|
|
9
|
+
/** The RFC 9162 leaf hash of an epoch leaf: `SHA-256(0x00 ‖ UTF-8(JCS(leaf)))`. */
|
|
10
|
+
export declare function epochLeafHash(leaf: EpochLeaf): Hash;
|
|
11
|
+
/** An STH signed with the directory's epoch key. Throws `invalid_request`. */
|
|
12
|
+
export declare function signSth(key: KeyObject, head: {
|
|
13
|
+
realm: RealmId;
|
|
14
|
+
size: number;
|
|
15
|
+
root: Hash;
|
|
16
|
+
ts: number;
|
|
17
|
+
}): STH;
|
|
18
|
+
/** True when `sth` is signed by `key`. Never throws. */
|
|
19
|
+
export declare function verifySthSignature(sth: STH, key: string): boolean;
|
|
20
|
+
/** The anchor an envelope carries for this STH. */
|
|
21
|
+
export declare function epochAnchor(sth: STH): EpochAnchor;
|
|
22
|
+
/** A rotation of the epoch key from `oldKey` to `newKey`, signed by both, from `fromSize` on. Throws `invalid_request`. */
|
|
23
|
+
export declare function signKeyRotation(oldKey: KeyObject, newKey: KeyObject, args: {
|
|
24
|
+
realm: RealmId;
|
|
25
|
+
fromSize: number;
|
|
26
|
+
}): KeyRotation;
|
|
27
|
+
/**
|
|
28
|
+
* Checks that `proof` puts its leaf at its index in the tree of `sth` (the leaf's `epoch` is its index). Throws
|
|
29
|
+
* `invalid_request` for a malformed proof and `directory_inconsistent` when it does not prove the leaf.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verifyEpochProof(sth: STH, proof: unknown): EpochProof;
|
|
32
|
+
/** What a client holds of the epoch log: the STH it adopted and the epoch key it trusts. */
|
|
33
|
+
export interface AdoptedHead {
|
|
34
|
+
readonly sth: STH;
|
|
35
|
+
readonly key: string;
|
|
36
|
+
}
|
|
37
|
+
export interface AdoptArgs {
|
|
38
|
+
readonly realm: RealmId;
|
|
39
|
+
/** The STH to adopt. */
|
|
40
|
+
readonly next: unknown;
|
|
41
|
+
/** RFC 9162 consistency proof from the adopted STH's size to `next`'s. */
|
|
42
|
+
readonly consistency?: readonly Hash[] | undefined;
|
|
43
|
+
/** Rotation statements from the trusted key to the key that signed `next`, oldest first. */
|
|
44
|
+
readonly rotations?: readonly unknown[] | undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The checks before a client adopts a newer STH (spec §15.6): the schema (`invalid_request`); the realm; the
|
|
48
|
+
* signature by the trusted key, or by a key that rotation statements reach from it; `size` and `ts` not below
|
|
49
|
+
* the adopted STH's; and the consistency proof from the adopted STH (equal sizes: equal roots). Every other
|
|
50
|
+
* failure is `directory_inconsistent`. `current` is undefined for the first STH, which only the pinned key
|
|
51
|
+
* (`pinnedKey`) may sign, or a key its rotations reach.
|
|
52
|
+
*/
|
|
53
|
+
export declare function adoptSth(current: AdoptedHead | undefined, pinnedKey: string, args: AdoptArgs): AdoptedHead;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lower-case the local part, map every run of characters outside `[a-z0-9]` to `-`, trim `-`, and, when the
|
|
3
|
+
* result does not start with a letter, prefix `u-`; then cut to 32 characters and trim a trailing `-`. An empty
|
|
4
|
+
* result is `u`. The result always matches `[a-z][a-z0-9-]{0,31}`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function hostedHandle(email: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* The org of a verified email until Jelly contract v1.2 (spec §15.10): `domain:<d>` when its domain `d` is one of
|
|
9
|
+
* the realm's `orgDomains`, otherwise none (an individual).
|
|
10
|
+
*/
|
|
11
|
+
export declare function emailOrg(email: string | undefined, orgDomains: readonly string[]): string | undefined;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { MachineId } from "../schema/common.js";
|
|
2
|
+
/** RFC 8628's device grant type. */
|
|
3
|
+
export declare const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
4
|
+
/** RFC 8628 §3.5, v1.2 §12.3: `slow_down` adds 5 s to the interval. The interval is kept within [1, 60] s. */
|
|
5
|
+
export declare const LOGIN_SLOW_DOWN_S = 5;
|
|
6
|
+
export declare const LOGIN_INTERVAL_MAX_S = 60;
|
|
7
|
+
/** The most bytes of an answer from Jelly ID that the daemon reads. */
|
|
8
|
+
export declare const JELLY_ANSWER_BYTES = 65536;
|
|
9
|
+
/**
|
|
10
|
+
* The key's fingerprint as Jelly ID's consent page shows it (v1.2 §12.2, item 3): the first six bytes of the RFC 7638
|
|
11
|
+
* thumbprint of `{kty: "OKP", crv: "Ed25519", x}` (SHA-256 over its members in that RFC's order), in lower-case hex,
|
|
12
|
+
* in three groups of four.
|
|
13
|
+
*/
|
|
14
|
+
export declare function jellyKeyFingerprint(device: MachineId): string;
|
|
15
|
+
/** Where a device signs in, read from Jelly ID's metadata. */
|
|
16
|
+
export interface DeviceSignIn {
|
|
17
|
+
/** `jelly_resource_device_authorization_endpoint`. */
|
|
18
|
+
readonly codeEndpoint: string;
|
|
19
|
+
/** `jelly_resource_device_token_endpoint`. */
|
|
20
|
+
readonly tokenEndpoint: string;
|
|
21
|
+
/** The resource's `name` in `jelly_resources`: the device client's `client_id`. */
|
|
22
|
+
readonly clientId: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Reads device sign-in from Jelly ID's RFC 8414 metadata (`<issuer>/.well-known/oauth-authorization-server`, as
|
|
26
|
+
* text). It needs the two members of v1.2 §12 (`jelly_resource_device_authorization_endpoint`, `…_token_endpoint`), both on the issuer's own origin, and the realm's resource in
|
|
27
|
+
* `jelly_resources`. It never takes RFC 8628's `device_authorization_endpoint`: at Jelly ID that route enrols hosts.
|
|
28
|
+
* Throws `login_unsupported` (Jelly ID lacks it) or `jelly_unavailable` (an answer that is not metadata for `issuer`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function readDeviceSignIn(text: string, realm: {
|
|
31
|
+
readonly issuer: string;
|
|
32
|
+
readonly resource: string;
|
|
33
|
+
}): DeviceSignIn;
|
|
34
|
+
/** Where the person signs in and what they type there. Never a link with the code in it. */
|
|
35
|
+
export interface DeviceCodeAnswer {
|
|
36
|
+
readonly deviceCode: string;
|
|
37
|
+
readonly userCode: string;
|
|
38
|
+
/** Jelly ID's `/link` page, where the person types `userCode`. */
|
|
39
|
+
readonly verificationUri: string;
|
|
40
|
+
readonly expiresInS: number;
|
|
41
|
+
readonly intervalS: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reads Jelly ID's answer to the code request (v1.2 §12.1). Every refusal Jelly ID defines throws in the house style:
|
|
45
|
+
* - `429 slow_down` (too many codes from this address, or waiting for this client): `rate_limited`;
|
|
46
|
+
* - `400 invalid_target` with `detail: "invalid_cnf_jwk"` (Jelly would not bind this key): `binding_invalid`;
|
|
47
|
+
* - `400 invalid_target` (not this client's resource), `401 invalid_client` (not a device client, or device sign-in
|
|
48
|
+
* off for it): `login_unsupported`;
|
|
49
|
+
* - `400 invalid_request` (a field missing, repeated or malformed): `invalid_request`;
|
|
50
|
+
* - `503 temporarily_unavailable`, and anything Jelly ID does not define: `jelly_unavailable`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function readDeviceCodeAnswer(status: number, text: string, issuer: string): DeviceCodeAnswer;
|
|
53
|
+
/** What a poll of the token route says (RFC 8628 §3.5; v1.2 §12.3). */
|
|
54
|
+
export type DevicePoll = {
|
|
55
|
+
readonly kind: "token";
|
|
56
|
+
readonly token: string;
|
|
57
|
+
} | {
|
|
58
|
+
readonly kind: "pending";
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: "slow_down";
|
|
61
|
+
}
|
|
62
|
+
/** Jelly ID could not answer now (a 5xx, a timeout, an answer that is not one): keep polling until the code expires. */
|
|
63
|
+
| {
|
|
64
|
+
readonly kind: "retry";
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Reads a poll's answer (v1.2 §12.3). `authorization_pending` and `slow_down` keep polling. A final refusal throws:
|
|
68
|
+
* `login_denied` (`access_denied`: the person denied it, or the account is disabled or being deleted), `expired`
|
|
69
|
+
* (`expired_token`: the code's time passed, decided or not; or `invalid_grant`: the code was used, or Jelly ID no
|
|
70
|
+
* longer knows it), `login_unsupported` (`invalid_client`, `unsupported_grant_type`). `503 temporarily_unavailable`,
|
|
71
|
+
* and any answer Jelly ID does not define, is `retry`: the daemon polls on until the code expires.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readDevicePoll(status: number, text: string): DevicePoll;
|
|
74
|
+
/** The form of the code request (v1.2 §12.1). */
|
|
75
|
+
export declare function deviceCodeForm(args: {
|
|
76
|
+
clientId: string;
|
|
77
|
+
resource: string;
|
|
78
|
+
device: MachineId;
|
|
79
|
+
label: string;
|
|
80
|
+
kind: string;
|
|
81
|
+
}): URLSearchParams;
|
|
82
|
+
/** The form of a poll (v1.2 §12.3). */
|
|
83
|
+
export declare function devicePollForm(args: {
|
|
84
|
+
clientId: string;
|
|
85
|
+
deviceCode: string;
|
|
86
|
+
}): URLSearchParams;
|
|
87
|
+
/** For tests of the fingerprint's preimage: the RFC 7638 thumbprint, base64url. */
|
|
88
|
+
export declare function jellyKeyThumbprint(device: MachineId): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Jelly ID issuer double, for tests and the vectors only (exported from `@messaging/core/testing`): it signs a
|
|
3
|
+
* resource token as Jelly ID does (contract v1.1 §4). A JWS signs its own base64url text, not JCS, so this is
|
|
4
|
+
* Jelly's format and not a Messaging signature.
|
|
5
|
+
*/
|
|
6
|
+
import { type KeyObject } from "node:crypto";
|
|
7
|
+
/** A compact JWS over `header` and `claims` as given (members in their insertion order), signed with `key`. */
|
|
8
|
+
export declare function signJellyToken(key: KeyObject, header: Record<string, unknown>, claims: Record<string, unknown>): string;
|