@medicine-wheel/community-identity 0.14.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/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @medicine-wheel/community-identity
2
+
3
+ Who is in the circle. A person is a `human` node on the wheel with `metadata.kind = 'person'` and a `role`; a circle is a `circle` node; membership is a `member_of` edge; ceremonies held in a circle carry `circle_id`. Tokens and invitation codes are kept by the consumer (hashed, jsonl or your own store), never on the wheel.
4
+
5
+ Roles and the permission map are copied from STPB (`lib/types/roles.ts`): `participant → emerging_guide → ceremony_facilitator → firekeeper → admin`, with `story_keeper` parallel to firekeeper, plus `companion_ai` and `integration_ai`.
6
+
7
+ ```ts
8
+ import { personNode, circleNode, membershipEdge, hasPermission, JsonlCredentialStore, JsonlInvitationStore } from '@medicine-wheel/community-identity';
9
+
10
+ const node = personNode({ name: 'Guillaume', role: 'admin' }); // POST to /api/nodes
11
+ const circle = circleNode({ name: 'Ep349 circle', intention: '…', facilitator_id: node.id });
12
+ const edge = membershipEdge(node.id, circle.id, 'facilitator'); // POST to /api/edges
13
+ hasPermission('ceremony_facilitator', 'invite_members'); // true
14
+
15
+ const creds = new JsonlCredentialStore('/srv/miadi/identity/credentials.jsonl');
16
+ const { token } = await creds.issue(node.id, 'laptop'); // shown once
17
+ const record = await creds.verify(token); // who is calling
18
+ ```
19
+
20
+ Added in 0.14.0 for jgwill/Miadi#647 (episode 349). Spec: `rispecs/community-identity.spec.md`.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * A circle is a node on the wheel of type `circle`; membership is a relation.
3
+ *
4
+ * Copied in shape from STPB's `StoryCircle` (`lib/community/types.ts`:
5
+ * facilitator, participants, invited, capacity, sacred-container config) and
6
+ * mapped onto the wheel: the circle is a node, each member is an edge
7
+ * `member_of` from the person to the circle, the facilitator is the edge whose
8
+ * obligations name `facilitate`. Ceremonies held in the circle carry its id
9
+ * in `circle_id` (ontology-core 0.14.0).
10
+ */
11
+ import type { DirectionName, RelationalEdge, RelationalNode } from '@medicine-wheel/ontology-core';
12
+ import { z } from 'zod';
13
+ export declare const CIRCLE_KIND: "circle";
14
+ export declare const MEMBER_OF: "member_of";
15
+ /** What protects the space. A subset of STPB's SacredContainerConfig, the part a wheel can honour. */
16
+ export interface CircleContainer {
17
+ /** New ceremonies and beats in this circle are private to its members until shared. */
18
+ private_by_default: boolean;
19
+ /** Sharing outside the circle needs the speaker's explicit consent. */
20
+ explicit_sharing_consent: boolean;
21
+ /** No counts, no rankings shown to members. */
22
+ hide_engagement_metrics: boolean;
23
+ }
24
+ export declare const DEFAULT_CONTAINER: CircleContainer;
25
+ export interface Circle {
26
+ /** The node id on the wheel. */
27
+ id: string;
28
+ name: string;
29
+ intention: string;
30
+ facilitator_id: string;
31
+ direction?: DirectionName;
32
+ capacity?: number;
33
+ container: CircleContainer;
34
+ created_at: string;
35
+ updated_at: string;
36
+ }
37
+ export declare const NewCircleSchema: z.ZodObject<{
38
+ id: z.ZodOptional<z.ZodString>;
39
+ name: z.ZodString;
40
+ intention: z.ZodString;
41
+ facilitator_id: z.ZodString;
42
+ direction: z.ZodOptional<z.ZodEnum<["east", "south", "west", "north"]>>;
43
+ capacity: z.ZodOptional<z.ZodNumber>;
44
+ container: z.ZodOptional<z.ZodObject<{
45
+ private_by_default: z.ZodOptional<z.ZodBoolean>;
46
+ explicit_sharing_consent: z.ZodOptional<z.ZodBoolean>;
47
+ hide_engagement_metrics: z.ZodOptional<z.ZodBoolean>;
48
+ }, "strip", z.ZodTypeAny, {
49
+ private_by_default?: boolean | undefined;
50
+ explicit_sharing_consent?: boolean | undefined;
51
+ hide_engagement_metrics?: boolean | undefined;
52
+ }, {
53
+ private_by_default?: boolean | undefined;
54
+ explicit_sharing_consent?: boolean | undefined;
55
+ hide_engagement_metrics?: boolean | undefined;
56
+ }>>;
57
+ }, "strip", z.ZodTypeAny, {
58
+ name: string;
59
+ intention: string;
60
+ facilitator_id: string;
61
+ id?: string | undefined;
62
+ direction?: "east" | "south" | "west" | "north" | undefined;
63
+ capacity?: number | undefined;
64
+ container?: {
65
+ private_by_default?: boolean | undefined;
66
+ explicit_sharing_consent?: boolean | undefined;
67
+ hide_engagement_metrics?: boolean | undefined;
68
+ } | undefined;
69
+ }, {
70
+ name: string;
71
+ intention: string;
72
+ facilitator_id: string;
73
+ id?: string | undefined;
74
+ direction?: "east" | "south" | "west" | "north" | undefined;
75
+ capacity?: number | undefined;
76
+ container?: {
77
+ private_by_default?: boolean | undefined;
78
+ explicit_sharing_consent?: boolean | undefined;
79
+ hide_engagement_metrics?: boolean | undefined;
80
+ } | undefined;
81
+ }>;
82
+ export type NewCircle = z.infer<typeof NewCircleSchema>;
83
+ export declare function circleNodeId(): string;
84
+ export declare function circleNode(input: NewCircle, now?: string): RelationalNode;
85
+ export declare function circleFromNode(node: RelationalNode | null | undefined): Circle | null;
86
+ export type CircleRole = 'member' | 'facilitator';
87
+ export interface Membership {
88
+ person_id: string;
89
+ circle_id: string;
90
+ role: CircleRole;
91
+ since: string;
92
+ }
93
+ /** The edge to weave on the wheel when a person joins a circle. */
94
+ export declare function membershipEdge(person_id: string, circle_id: string, role?: CircleRole, now?: string): Omit<RelationalEdge, 'id'>;
95
+ export declare function membershipFromEdge(edge: RelationalEdge | Omit<RelationalEdge, 'id'>): Membership | null;
96
+ /** Members of one circle, from the edges that point at it. */
97
+ export declare function membersOf(circle_id: string, edges: readonly (RelationalEdge | Omit<RelationalEdge, 'id'>)[]): Membership[];
98
+ /** Circles one person belongs to, from the edges that leave them. */
99
+ export declare function circlesOf(person_id: string, edges: readonly (RelationalEdge | Omit<RelationalEdge, 'id'>)[]): Membership[];
100
+ //# sourceMappingURL=circles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circles.d.ts","sourceRoot":"","sources":["../src/circles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACnG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,WAAW,EAAG,QAAiB,CAAC;AAC7C,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC;AAE9C,sGAAsG;AACtG,MAAM,WAAW,eAAe;IAC9B,uFAAuF;IACvF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,uEAAuE;IACvE,wBAAwB,EAAE,OAAO,CAAC;IAClC,+CAA+C;IAC/C,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,iBAAiB,EAAE,eAI/B,CAAC;AAEF,MAAM,WAAW,MAAM;IACrB,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,eAAe,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAc1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,SAA2B,GAAG,cAAc,CAgB3F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAgBrF;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,aAAa,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,UAAqB,EAAE,GAAG,SAA2B,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAU5J;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAQvG;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAK1H;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAK1H"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ /**
3
+ * A circle is a node on the wheel of type `circle`; membership is a relation.
4
+ *
5
+ * Copied in shape from STPB's `StoryCircle` (`lib/community/types.ts`:
6
+ * facilitator, participants, invited, capacity, sacred-container config) and
7
+ * mapped onto the wheel: the circle is a node, each member is an edge
8
+ * `member_of` from the person to the circle, the facilitator is the edge whose
9
+ * obligations name `facilitate`. Ceremonies held in the circle carry its id
10
+ * in `circle_id` (ontology-core 0.14.0).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.NewCircleSchema = exports.DEFAULT_CONTAINER = exports.MEMBER_OF = exports.CIRCLE_KIND = void 0;
14
+ exports.circleNodeId = circleNodeId;
15
+ exports.circleNode = circleNode;
16
+ exports.circleFromNode = circleFromNode;
17
+ exports.membershipEdge = membershipEdge;
18
+ exports.membershipFromEdge = membershipFromEdge;
19
+ exports.membersOf = membersOf;
20
+ exports.circlesOf = circlesOf;
21
+ const zod_1 = require("zod");
22
+ exports.CIRCLE_KIND = 'circle';
23
+ exports.MEMBER_OF = 'member_of';
24
+ exports.DEFAULT_CONTAINER = {
25
+ private_by_default: true,
26
+ explicit_sharing_consent: true,
27
+ hide_engagement_metrics: true,
28
+ };
29
+ exports.NewCircleSchema = zod_1.z.object({
30
+ id: zod_1.z.string().trim().min(1).optional(),
31
+ name: zod_1.z.string().trim().min(1),
32
+ intention: zod_1.z.string().trim().min(1),
33
+ facilitator_id: zod_1.z.string().trim().min(1),
34
+ direction: zod_1.z.enum(['east', 'south', 'west', 'north']).optional(),
35
+ capacity: zod_1.z.number().int().positive().optional(),
36
+ container: zod_1.z
37
+ .object({
38
+ private_by_default: zod_1.z.boolean().optional(),
39
+ explicit_sharing_consent: zod_1.z.boolean().optional(),
40
+ hide_engagement_metrics: zod_1.z.boolean().optional(),
41
+ })
42
+ .optional(),
43
+ });
44
+ function circleNodeId() {
45
+ return `circle:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
46
+ }
47
+ function circleNode(input, now = new Date().toISOString()) {
48
+ return {
49
+ id: input.id ?? circleNodeId(),
50
+ name: input.name,
51
+ type: 'circle',
52
+ ...(input.direction ? { direction: input.direction } : {}),
53
+ metadata: {
54
+ kind: exports.CIRCLE_KIND,
55
+ intention: input.intention,
56
+ facilitator_id: input.facilitator_id,
57
+ ...(input.capacity !== undefined ? { capacity: input.capacity } : {}),
58
+ container: { ...exports.DEFAULT_CONTAINER, ...(input.container ?? {}) },
59
+ },
60
+ created_at: now,
61
+ updated_at: now,
62
+ };
63
+ }
64
+ function circleFromNode(node) {
65
+ if (!node || node.type !== 'circle')
66
+ return null;
67
+ const meta = node.metadata ?? {};
68
+ if (typeof meta.facilitator_id !== 'string')
69
+ return null;
70
+ const container = (meta.container && typeof meta.container === 'object' ? meta.container : {});
71
+ return {
72
+ id: node.id,
73
+ name: node.name,
74
+ intention: typeof meta.intention === 'string' ? meta.intention : '',
75
+ facilitator_id: meta.facilitator_id,
76
+ ...(node.direction ? { direction: node.direction } : {}),
77
+ ...(typeof meta.capacity === 'number' ? { capacity: meta.capacity } : {}),
78
+ container: { ...exports.DEFAULT_CONTAINER, ...container },
79
+ created_at: node.created_at,
80
+ updated_at: node.updated_at,
81
+ };
82
+ }
83
+ /** The edge to weave on the wheel when a person joins a circle. */
84
+ function membershipEdge(person_id, circle_id, role = 'member', now = new Date().toISOString()) {
85
+ return {
86
+ from_id: person_id,
87
+ to_id: circle_id,
88
+ relationship_type: exports.MEMBER_OF,
89
+ strength: 1,
90
+ ceremony_honored: false,
91
+ obligations: role === 'facilitator' ? ['facilitate', 'hold the space'] : ['show up', 'listen'],
92
+ created_at: now,
93
+ };
94
+ }
95
+ function membershipFromEdge(edge) {
96
+ if (edge.relationship_type !== exports.MEMBER_OF)
97
+ return null;
98
+ return {
99
+ person_id: edge.from_id,
100
+ circle_id: edge.to_id,
101
+ role: edge.obligations?.includes('facilitate') ? 'facilitator' : 'member',
102
+ since: edge.created_at,
103
+ };
104
+ }
105
+ /** Members of one circle, from the edges that point at it. */
106
+ function membersOf(circle_id, edges) {
107
+ return edges
108
+ .filter((e) => e.to_id === circle_id)
109
+ .map(membershipFromEdge)
110
+ .filter((m) => m !== null);
111
+ }
112
+ /** Circles one person belongs to, from the edges that leave them. */
113
+ function circlesOf(person_id, edges) {
114
+ return edges
115
+ .filter((e) => e.from_id === person_id)
116
+ .map(membershipFromEdge)
117
+ .filter((m) => m !== null);
118
+ }
119
+ //# sourceMappingURL=circles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circles.js","sourceRoot":"","sources":["../src/circles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Credentials: the secret that proves a person is who the wheel says.
3
+ *
4
+ * Never stored on the wheel. The wheel holds the relational truth (who, in
5
+ * which circle, with which role); the consumer that lets people sign in holds
6
+ * the secrets, hashed. A person issues their own tokens (STPB's
7
+ * `api/user/tokens`), an admin issues the first one for a newcomer.
8
+ *
9
+ * The store is an interface so a community can keep credentials where it
10
+ * chooses. `JsonlCredentialStore` is the file-backed one Miadi uses.
11
+ */
12
+ export declare const TOKEN_PREFIX = "mwt_";
13
+ export interface CredentialRecord {
14
+ id: string;
15
+ person_id: string;
16
+ label: string;
17
+ /** sha256 hex of the token. The token itself is shown once, at issue. */
18
+ token_hash: string;
19
+ created_at: string;
20
+ last_used_at?: string;
21
+ revoked_at?: string;
22
+ }
23
+ export interface IssuedCredential {
24
+ /** The bearer token, shown once. */
25
+ token: string;
26
+ record: CredentialRecord;
27
+ }
28
+ export interface CredentialStore {
29
+ issue(person_id: string, label: string): Promise<IssuedCredential>;
30
+ /** The live record for a token, or null when unknown or revoked. Touches `last_used_at`. */
31
+ verify(token: string): Promise<CredentialRecord | null>;
32
+ list(person_id: string): Promise<CredentialRecord[]>;
33
+ /** Revoke by record id. Returns false when unknown. */
34
+ revoke(id: string, person_id?: string): Promise<boolean>;
35
+ }
36
+ export declare function mintToken(): string;
37
+ export declare function hashToken(token: string): string;
38
+ /**
39
+ * One append-only jsonl file: issues append, revocations and last-use rewrite
40
+ * the record in place. Small by design; a community with thousands of people
41
+ * puts the same interface over a database.
42
+ */
43
+ export declare class JsonlCredentialStore implements CredentialStore {
44
+ private readonly file;
45
+ constructor(file: string);
46
+ private readAll;
47
+ private writeAll;
48
+ issue(person_id: string, label: string): Promise<IssuedCredential>;
49
+ verify(token: string): Promise<CredentialRecord | null>;
50
+ list(person_id: string): Promise<CredentialRecord[]>;
51
+ revoke(id: string, person_id?: string): Promise<boolean>;
52
+ }
53
+ /** Public view of a credential: never the hash. */
54
+ export declare function publicCredential(record: CredentialRecord): Omit<CredentialRecord, 'token_hash'>;
55
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnE,4FAA4F;IAC5F,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrD,uDAAuD;IACvD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAYD;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAEzC,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,QAAQ;IAKV,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAelE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAWvD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAIpD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAU/D;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAG/F"}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /**
3
+ * Credentials: the secret that proves a person is who the wheel says.
4
+ *
5
+ * Never stored on the wheel. The wheel holds the relational truth (who, in
6
+ * which circle, with which role); the consumer that lets people sign in holds
7
+ * the secrets, hashed. A person issues their own tokens (STPB's
8
+ * `api/user/tokens`), an admin issues the first one for a newcomer.
9
+ *
10
+ * The store is an interface so a community can keep credentials where it
11
+ * chooses. `JsonlCredentialStore` is the file-backed one Miadi uses.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.JsonlCredentialStore = exports.TOKEN_PREFIX = void 0;
15
+ exports.mintToken = mintToken;
16
+ exports.hashToken = hashToken;
17
+ exports.publicCredential = publicCredential;
18
+ const node_crypto_1 = require("node:crypto");
19
+ const node_fs_1 = require("node:fs");
20
+ const node_path_1 = require("node:path");
21
+ exports.TOKEN_PREFIX = 'mwt_';
22
+ function mintToken() {
23
+ return exports.TOKEN_PREFIX + (0, node_crypto_1.randomBytes)(32).toString('base64url');
24
+ }
25
+ function hashToken(token) {
26
+ return (0, node_crypto_1.createHash)('sha256').update(token, 'utf8').digest('hex');
27
+ }
28
+ function sameHash(a, b) {
29
+ const ba = Buffer.from(a, 'hex');
30
+ const bb = Buffer.from(b, 'hex');
31
+ return ba.length === bb.length && (0, node_crypto_1.timingSafeEqual)(ba, bb);
32
+ }
33
+ function credentialId() {
34
+ return `cred:${Date.now()}:${(0, node_crypto_1.randomBytes)(3).toString('hex')}`;
35
+ }
36
+ /**
37
+ * One append-only jsonl file: issues append, revocations and last-use rewrite
38
+ * the record in place. Small by design; a community with thousands of people
39
+ * puts the same interface over a database.
40
+ */
41
+ class JsonlCredentialStore {
42
+ file;
43
+ constructor(file) {
44
+ this.file = file;
45
+ }
46
+ readAll() {
47
+ if (!(0, node_fs_1.existsSync)(this.file))
48
+ return [];
49
+ return (0, node_fs_1.readFileSync)(this.file, 'utf8')
50
+ .split('\n')
51
+ .filter((line) => line.trim())
52
+ .map((line) => JSON.parse(line));
53
+ }
54
+ writeAll(records) {
55
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.file), { recursive: true });
56
+ (0, node_fs_1.writeFileSync)(this.file, records.map((r) => JSON.stringify(r)).join('\n') + (records.length ? '\n' : ''), { mode: 0o600 });
57
+ }
58
+ async issue(person_id, label) {
59
+ const token = mintToken();
60
+ const record = {
61
+ id: credentialId(),
62
+ person_id,
63
+ label: label.trim() || 'token',
64
+ token_hash: hashToken(token),
65
+ created_at: new Date().toISOString(),
66
+ };
67
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.file), { recursive: true });
68
+ if (!(0, node_fs_1.existsSync)(this.file))
69
+ (0, node_fs_1.writeFileSync)(this.file, '', { mode: 0o600 });
70
+ (0, node_fs_1.appendFileSync)(this.file, JSON.stringify(record) + '\n');
71
+ return { token, record };
72
+ }
73
+ async verify(token) {
74
+ if (!token || !token.startsWith(exports.TOKEN_PREFIX))
75
+ return null;
76
+ const hash = hashToken(token);
77
+ const records = this.readAll();
78
+ const index = records.findIndex((r) => !r.revoked_at && sameHash(r.token_hash, hash));
79
+ if (index < 0)
80
+ return null;
81
+ records[index] = { ...records[index], last_used_at: new Date().toISOString() };
82
+ this.writeAll(records);
83
+ return records[index];
84
+ }
85
+ async list(person_id) {
86
+ return this.readAll().filter((r) => r.person_id === person_id);
87
+ }
88
+ async revoke(id, person_id) {
89
+ const records = this.readAll();
90
+ const index = records.findIndex((r) => r.id === id && (person_id === undefined || r.person_id === person_id));
91
+ if (index < 0)
92
+ return false;
93
+ if (!records[index].revoked_at) {
94
+ records[index] = { ...records[index], revoked_at: new Date().toISOString() };
95
+ this.writeAll(records);
96
+ }
97
+ return true;
98
+ }
99
+ }
100
+ exports.JsonlCredentialStore = JsonlCredentialStore;
101
+ /** Public view of a credential: never the hash. */
102
+ function publicCredential(record) {
103
+ const { token_hash: _hash, ...rest } = record;
104
+ return rest;
105
+ }
106
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @medicine-wheel/community-identity
3
+ *
4
+ * Measured 2026-09-03 (`rispecs/community-identity.spec.md`): neither the wheel
5
+ * nor Miadi could tell two people apart. This package is the smallest set of
6
+ * facts that lets them: a person is a `human` node with a role, a circle is a
7
+ * `circle` node, membership is a `member_of` relation, and the secrets
8
+ * (tokens, invitation codes) are kept by the consumer, hashed, never on the
9
+ * wheel. Roles and their permission map are copied from STPB.
10
+ *
11
+ * What this package does not decide (the spec's held question): whether a
12
+ * published, unpublishable-after-72h npm package can honour OCAP's right to
13
+ * withdraw. It carries no person's data; it carries the shape.
14
+ */
15
+ export * from './roles.js';
16
+ export * from './people.js';
17
+ export * from './circles.js';
18
+ export * from './credentials.js';
19
+ export * from './invitations.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * @medicine-wheel/community-identity
4
+ *
5
+ * Measured 2026-09-03 (`rispecs/community-identity.spec.md`): neither the wheel
6
+ * nor Miadi could tell two people apart. This package is the smallest set of
7
+ * facts that lets them: a person is a `human` node with a role, a circle is a
8
+ * `circle` node, membership is a `member_of` relation, and the secrets
9
+ * (tokens, invitation codes) are kept by the consumer, hashed, never on the
10
+ * wheel. Roles and their permission map are copied from STPB.
11
+ *
12
+ * What this package does not decide (the spec's held question): whether a
13
+ * published, unpublishable-after-72h npm package can honour OCAP's right to
14
+ * withdraw. It carries no person's data; it carries the shape.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
28
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
29
+ };
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ __exportStar(require("./roles.js"), exports);
32
+ __exportStar(require("./people.js"), exports);
33
+ __exportStar(require("./circles.js"), exports);
34
+ __exportStar(require("./credentials.js"), exports);
35
+ __exportStar(require("./invitations.js"), exports);
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;AAEH,6CAA2B;AAC3B,8CAA4B;AAC5B,+CAA6B;AAC7B,mDAAiC;AACjC,mDAAiC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Invitations: the door into a circle.
3
+ *
4
+ * STPB invites by circle (`circles/[id]/invite`) and admits by ceremony code
5
+ * (`admin/ceremony-codes`). Here one record does both: a code, minted by
6
+ * someone allowed to invite, bound to a circle, with a role for whoever
7
+ * accepts it. Codes are operational, not relational, so like credentials they
8
+ * stay with the consumer, not on the wheel; accepting one weaves the
9
+ * `member_of` edge, which is the relational fact.
10
+ */
11
+ import type { CircleRole } from './circles.js';
12
+ export interface InvitationRecord {
13
+ code: string;
14
+ circle_id: string;
15
+ invited_by: string;
16
+ role: CircleRole;
17
+ /** A name or email the invitation was meant for; informational. */
18
+ intended_for?: string;
19
+ created_at: string;
20
+ expires_at?: string;
21
+ max_uses: number;
22
+ accepted_by: string[];
23
+ revoked_at?: string;
24
+ }
25
+ export interface InvitationStore {
26
+ create(input: {
27
+ circle_id: string;
28
+ invited_by: string;
29
+ role?: CircleRole;
30
+ intended_for?: string;
31
+ expires_at?: string;
32
+ max_uses?: number;
33
+ }): Promise<InvitationRecord>;
34
+ get(code: string): Promise<InvitationRecord | null>;
35
+ listForCircle(circle_id: string): Promise<InvitationRecord[]>;
36
+ /** Record an acceptance. Returns the record, or a reason it could not be accepted. */
37
+ accept(code: string, person_id: string): Promise<{
38
+ ok: true;
39
+ record: InvitationRecord;
40
+ } | {
41
+ ok: false;
42
+ reason: 'unknown' | 'revoked' | 'expired' | 'exhausted' | 'already';
43
+ }>;
44
+ revoke(code: string): Promise<boolean>;
45
+ }
46
+ export declare function mintInvitationCode(length?: number): string;
47
+ export declare function invitationState(record: InvitationRecord, now?: number): 'open' | 'revoked' | 'expired' | 'exhausted';
48
+ export declare class JsonlInvitationStore implements InvitationStore {
49
+ private readonly file;
50
+ constructor(file: string);
51
+ private readAll;
52
+ private writeAll;
53
+ create(input: {
54
+ circle_id: string;
55
+ invited_by: string;
56
+ role?: CircleRole;
57
+ intended_for?: string;
58
+ expires_at?: string;
59
+ max_uses?: number;
60
+ }): Promise<InvitationRecord>;
61
+ get(code: string): Promise<InvitationRecord | null>;
62
+ listForCircle(circle_id: string): Promise<InvitationRecord[]>;
63
+ accept(code: string, person_id: string): Promise<{
64
+ ok: false;
65
+ reason: "unknown";
66
+ record?: undefined;
67
+ } | {
68
+ ok: false;
69
+ reason: "already";
70
+ record?: undefined;
71
+ } | {
72
+ ok: false;
73
+ reason: "revoked" | "expired" | "exhausted";
74
+ record?: undefined;
75
+ } | {
76
+ ok: true;
77
+ record: InvitationRecord;
78
+ reason?: undefined;
79
+ }>;
80
+ revoke(code: string): Promise<boolean>;
81
+ }
82
+ //# sourceMappingURL=invitations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitations.d.ts","sourceRoot":"","sources":["../src/invitations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,UAAU,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtK,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC9D,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAC9K,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAID,wBAAgB,kBAAkB,CAAC,MAAM,SAAI,GAAG,MAAM,CAKrD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,SAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAKxH;AAED,qBAAa,oBAAqB,YAAW,eAAe;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAEzC,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,QAAQ;IAKV,MAAM,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,UAAU,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiBrK,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAInD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAI7D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;IAatC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAU7C"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Invitations: the door into a circle.
4
+ *
5
+ * STPB invites by circle (`circles/[id]/invite`) and admits by ceremony code
6
+ * (`admin/ceremony-codes`). Here one record does both: a code, minted by
7
+ * someone allowed to invite, bound to a circle, with a role for whoever
8
+ * accepts it. Codes are operational, not relational, so like credentials they
9
+ * stay with the consumer, not on the wheel; accepting one weaves the
10
+ * `member_of` edge, which is the relational fact.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.JsonlInvitationStore = void 0;
14
+ exports.mintInvitationCode = mintInvitationCode;
15
+ exports.invitationState = invitationState;
16
+ const node_crypto_1 = require("node:crypto");
17
+ const node_fs_1 = require("node:fs");
18
+ const node_path_1 = require("node:path");
19
+ const ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
20
+ function mintInvitationCode(length = 8) {
21
+ const bytes = (0, node_crypto_1.randomBytes)(length);
22
+ let out = '';
23
+ for (let i = 0; i < length; i++)
24
+ out += ALPHABET[bytes[i] % ALPHABET.length];
25
+ return out;
26
+ }
27
+ function invitationState(record, now = Date.now()) {
28
+ if (record.revoked_at)
29
+ return 'revoked';
30
+ if (record.expires_at && Date.parse(record.expires_at) < now)
31
+ return 'expired';
32
+ if (record.accepted_by.length >= record.max_uses)
33
+ return 'exhausted';
34
+ return 'open';
35
+ }
36
+ class JsonlInvitationStore {
37
+ file;
38
+ constructor(file) {
39
+ this.file = file;
40
+ }
41
+ readAll() {
42
+ if (!(0, node_fs_1.existsSync)(this.file))
43
+ return [];
44
+ return (0, node_fs_1.readFileSync)(this.file, 'utf8')
45
+ .split('\n')
46
+ .filter((line) => line.trim())
47
+ .map((line) => JSON.parse(line));
48
+ }
49
+ writeAll(records) {
50
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.file), { recursive: true });
51
+ (0, node_fs_1.writeFileSync)(this.file, records.map((r) => JSON.stringify(r)).join('\n') + (records.length ? '\n' : ''));
52
+ }
53
+ async create(input) {
54
+ const record = {
55
+ code: mintInvitationCode(),
56
+ circle_id: input.circle_id,
57
+ invited_by: input.invited_by,
58
+ role: input.role ?? 'member',
59
+ ...(input.intended_for ? { intended_for: input.intended_for } : {}),
60
+ created_at: new Date().toISOString(),
61
+ ...(input.expires_at ? { expires_at: input.expires_at } : {}),
62
+ max_uses: input.max_uses && input.max_uses > 0 ? input.max_uses : 1,
63
+ accepted_by: [],
64
+ };
65
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.file), { recursive: true });
66
+ (0, node_fs_1.appendFileSync)(this.file, JSON.stringify(record) + '\n');
67
+ return record;
68
+ }
69
+ async get(code) {
70
+ return this.readAll().find((r) => r.code === code.trim().toUpperCase()) ?? null;
71
+ }
72
+ async listForCircle(circle_id) {
73
+ return this.readAll().filter((r) => r.circle_id === circle_id);
74
+ }
75
+ async accept(code, person_id) {
76
+ const records = this.readAll();
77
+ const index = records.findIndex((r) => r.code === code.trim().toUpperCase());
78
+ if (index < 0)
79
+ return { ok: false, reason: 'unknown' };
80
+ const record = records[index];
81
+ if (record.accepted_by.includes(person_id))
82
+ return { ok: false, reason: 'already' };
83
+ const state = invitationState(record);
84
+ if (state !== 'open')
85
+ return { ok: false, reason: state };
86
+ records[index] = { ...record, accepted_by: [...record.accepted_by, person_id] };
87
+ this.writeAll(records);
88
+ return { ok: true, record: records[index] };
89
+ }
90
+ async revoke(code) {
91
+ const records = this.readAll();
92
+ const index = records.findIndex((r) => r.code === code.trim().toUpperCase());
93
+ if (index < 0)
94
+ return false;
95
+ if (!records[index].revoked_at) {
96
+ records[index] = { ...records[index], revoked_at: new Date().toISOString() };
97
+ this.writeAll(records);
98
+ }
99
+ return true;
100
+ }
101
+ }
102
+ exports.JsonlInvitationStore = JsonlInvitationStore;
103
+ //# sourceMappingURL=invitations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitations.js","sourceRoot":"","sources":["../src/invitations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * A person is a node on the wheel of type `human` whose metadata says so.
3
+ *
4
+ * The wheel had `human` nodes long before this package (participants in
5
+ * ceremonies were written as `node:human:<ts>:<rand>`); what it could not do
6
+ * was tell two of them apart or say what either may do. `metadata.kind =
7
+ * 'person'` and `metadata.role` are the two facts this package adds. The
8
+ * secret that proves a person is who they say (a token) never goes on the
9
+ * wheel; see `credentials.ts`.
10
+ */
11
+ import type { DirectionName, RelationalNode } from '@medicine-wheel/ontology-core';
12
+ import { z } from 'zod';
13
+ import { type Role } from './roles.js';
14
+ export declare const PERSON_KIND: "person";
15
+ export interface Person {
16
+ /** The node id on the wheel. */
17
+ id: string;
18
+ name: string;
19
+ role: Role;
20
+ email?: string;
21
+ direction?: DirectionName;
22
+ created_at: string;
23
+ updated_at: string;
24
+ }
25
+ export declare const NewPersonSchema: z.ZodObject<{
26
+ id: z.ZodOptional<z.ZodString>;
27
+ name: z.ZodString;
28
+ role: z.ZodEffects<z.ZodString, "participant" | "emerging_guide" | "ceremony_facilitator" | "firekeeper" | "story_keeper" | "admin" | "companion_ai" | "integration_ai", string>;
29
+ email: z.ZodOptional<z.ZodString>;
30
+ direction: z.ZodOptional<z.ZodEnum<["east", "south", "west", "north"]>>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ name: string;
33
+ role: "participant" | "emerging_guide" | "ceremony_facilitator" | "firekeeper" | "story_keeper" | "admin" | "companion_ai" | "integration_ai";
34
+ id?: string | undefined;
35
+ direction?: "east" | "south" | "west" | "north" | undefined;
36
+ email?: string | undefined;
37
+ }, {
38
+ name: string;
39
+ role: string;
40
+ id?: string | undefined;
41
+ direction?: "east" | "south" | "west" | "north" | undefined;
42
+ email?: string | undefined;
43
+ }>;
44
+ export type NewPerson = z.infer<typeof NewPersonSchema>;
45
+ export declare function personNodeId(): string;
46
+ /** The node to create on the wheel for a new person. */
47
+ export declare function personNode(input: NewPerson, now?: string): RelationalNode;
48
+ /** Read a person back from a node; null when the node is a human that this package does not govern. */
49
+ export declare function personFromNode(node: RelationalNode | null | undefined): Person | null;
50
+ /** The metadata patch that grants (or changes) a person's role. */
51
+ export declare function roleGrant(role: Role, current?: Record<string, unknown>): Record<string, unknown>;
52
+ //# sourceMappingURL=people.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"people.d.ts","sourceRoot":"","sources":["../src/people.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAU,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,WAAW,EAAG,QAAiB,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;EAM1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,SAA2B,GAAG,cAAc,CAc3F;AAED,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAarF;AAED,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEpG"}
package/dist/people.js ADDED
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * A person is a node on the wheel of type `human` whose metadata says so.
4
+ *
5
+ * The wheel had `human` nodes long before this package (participants in
6
+ * ceremonies were written as `node:human:<ts>:<rand>`); what it could not do
7
+ * was tell two of them apart or say what either may do. `metadata.kind =
8
+ * 'person'` and `metadata.role` are the two facts this package adds. The
9
+ * secret that proves a person is who they say (a token) never goes on the
10
+ * wheel; see `credentials.ts`.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.NewPersonSchema = exports.PERSON_KIND = void 0;
14
+ exports.personNodeId = personNodeId;
15
+ exports.personNode = personNode;
16
+ exports.personFromNode = personFromNode;
17
+ exports.roleGrant = roleGrant;
18
+ const zod_1 = require("zod");
19
+ const roles_js_1 = require("./roles.js");
20
+ exports.PERSON_KIND = 'person';
21
+ exports.NewPersonSchema = zod_1.z.object({
22
+ id: zod_1.z.string().trim().min(1).optional(),
23
+ name: zod_1.z.string().trim().min(1),
24
+ role: zod_1.z.string().refine(roles_js_1.isRole, 'unknown role'),
25
+ email: zod_1.z.string().trim().email().optional(),
26
+ direction: zod_1.z.enum(['east', 'south', 'west', 'north']).optional(),
27
+ });
28
+ function personNodeId() {
29
+ return `node:human:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
30
+ }
31
+ /** The node to create on the wheel for a new person. */
32
+ function personNode(input, now = new Date().toISOString()) {
33
+ return {
34
+ id: input.id ?? personNodeId(),
35
+ name: input.name,
36
+ type: 'human',
37
+ ...(input.direction ? { direction: input.direction } : {}),
38
+ metadata: {
39
+ kind: exports.PERSON_KIND,
40
+ role: input.role,
41
+ ...(input.email ? { email: input.email } : {}),
42
+ },
43
+ created_at: now,
44
+ updated_at: now,
45
+ };
46
+ }
47
+ /** Read a person back from a node; null when the node is a human that this package does not govern. */
48
+ function personFromNode(node) {
49
+ if (!node || node.type !== 'human')
50
+ return null;
51
+ const meta = node.metadata ?? {};
52
+ if (meta.kind !== exports.PERSON_KIND || !(0, roles_js_1.isRole)(meta.role))
53
+ return null;
54
+ return {
55
+ id: node.id,
56
+ name: node.name,
57
+ role: meta.role,
58
+ ...(typeof meta.email === 'string' ? { email: meta.email } : {}),
59
+ ...(node.direction ? { direction: node.direction } : {}),
60
+ created_at: node.created_at,
61
+ updated_at: node.updated_at,
62
+ };
63
+ }
64
+ /** The metadata patch that grants (or changes) a person's role. */
65
+ function roleGrant(role, current = {}) {
66
+ return { ...current, kind: exports.PERSON_KIND, role };
67
+ }
68
+ //# sourceMappingURL=people.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"people.js","sourceRoot":"","sources":["../src/people.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Roles and what each may do.
3
+ *
4
+ * Copied from STPB (`jgwill/StorytellingPlatformBlueprint2510`, `lib/types/roles.ts`)
5
+ * on 2026-09-18 and renamed where the name was a brand: `lighthouse_admin` is
6
+ * `admin`, `eva_ai` is `companion_ai`. The progression is the same one:
7
+ *
8
+ * participant → emerging_guide → ceremony_facilitator → firekeeper → admin
9
+ * ↘ story_keeper (parallel)
10
+ */
11
+ export declare const ROLES: readonly ["participant", "emerging_guide", "ceremony_facilitator", "firekeeper", "story_keeper", "admin", "companion_ai", "integration_ai"];
12
+ export type Role = (typeof ROLES)[number];
13
+ export declare const PERMISSIONS: readonly ["create_beats", "join_circles", "witness", "view_own_arc", "create_circles", "invite_members", "facilitate_ceremony", "moderate", "view_community_patterns", "view_historical_arcs", "create_wisdom", "propose_themes", "read_personal_data", "analyze_narrative", "read_public_api", "manage_people"];
14
+ export type Permission = (typeof PERMISSIONS)[number];
15
+ /** Story keeper is parallel to firekeeper, not above it. */
16
+ export declare const ROLE_HIERARCHY: Record<Role, number>;
17
+ export declare const ROLE_LABELS: Record<Role, string>;
18
+ export declare const ROLE_PERMISSIONS: Record<Role, readonly (Permission | '*')[]>;
19
+ export declare function isRole(value: unknown): value is Role;
20
+ export declare function hasPermission(role: Role, permission: Permission): boolean;
21
+ export declare function hasRoleLevel(role: Role, required: Role): boolean;
22
+ export declare function rolesAtOrAbove(role: Role): Role[];
23
+ //# sourceMappingURL=roles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,KAAK,6IASR,CAAC;AAEX,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1C,eAAO,MAAM,WAAW,kTAiBd,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAS/C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAS5C,CAAC;AAKF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,CASxE,CAAC;AAEF,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAGzE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAG,OAAO,CAEhE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,CAGjD"}
package/dist/roles.js ADDED
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * Roles and what each may do.
4
+ *
5
+ * Copied from STPB (`jgwill/StorytellingPlatformBlueprint2510`, `lib/types/roles.ts`)
6
+ * on 2026-09-18 and renamed where the name was a brand: `lighthouse_admin` is
7
+ * `admin`, `eva_ai` is `companion_ai`. The progression is the same one:
8
+ *
9
+ * participant → emerging_guide → ceremony_facilitator → firekeeper → admin
10
+ * ↘ story_keeper (parallel)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ROLE_PERMISSIONS = exports.ROLE_LABELS = exports.ROLE_HIERARCHY = exports.PERMISSIONS = exports.ROLES = void 0;
14
+ exports.isRole = isRole;
15
+ exports.hasPermission = hasPermission;
16
+ exports.hasRoleLevel = hasRoleLevel;
17
+ exports.rolesAtOrAbove = rolesAtOrAbove;
18
+ exports.ROLES = [
19
+ 'participant',
20
+ 'emerging_guide',
21
+ 'ceremony_facilitator',
22
+ 'firekeeper',
23
+ 'story_keeper',
24
+ 'admin',
25
+ 'companion_ai',
26
+ 'integration_ai',
27
+ ];
28
+ exports.PERMISSIONS = [
29
+ 'create_beats',
30
+ 'join_circles',
31
+ 'witness',
32
+ 'view_own_arc',
33
+ 'create_circles',
34
+ 'invite_members',
35
+ 'facilitate_ceremony',
36
+ 'moderate',
37
+ 'view_community_patterns',
38
+ 'view_historical_arcs',
39
+ 'create_wisdom',
40
+ 'propose_themes',
41
+ 'read_personal_data',
42
+ 'analyze_narrative',
43
+ 'read_public_api',
44
+ 'manage_people',
45
+ ];
46
+ /** Story keeper is parallel to firekeeper, not above it. */
47
+ exports.ROLE_HIERARCHY = {
48
+ participant: 0,
49
+ emerging_guide: 1,
50
+ ceremony_facilitator: 2,
51
+ firekeeper: 3,
52
+ story_keeper: 3,
53
+ admin: 4,
54
+ companion_ai: 0,
55
+ integration_ai: 0,
56
+ };
57
+ exports.ROLE_LABELS = {
58
+ participant: 'Participant',
59
+ emerging_guide: 'Emerging Guide',
60
+ ceremony_facilitator: 'Ceremony Facilitator',
61
+ firekeeper: 'Firekeeper',
62
+ story_keeper: 'Story Keeper',
63
+ admin: 'Admin',
64
+ companion_ai: 'Companion AI',
65
+ integration_ai: 'Integration AI',
66
+ };
67
+ const BASE = ['create_beats', 'join_circles'];
68
+ const GUIDE = [...BASE, 'witness', 'view_own_arc'];
69
+ exports.ROLE_PERMISSIONS = {
70
+ participant: BASE,
71
+ emerging_guide: GUIDE,
72
+ ceremony_facilitator: [...GUIDE, 'create_circles', 'invite_members', 'facilitate_ceremony'],
73
+ firekeeper: [...GUIDE, 'create_circles', 'invite_members', 'moderate', 'view_community_patterns'],
74
+ story_keeper: [...GUIDE, 'view_community_patterns', 'view_historical_arcs', 'create_wisdom', 'propose_themes'],
75
+ admin: ['*'],
76
+ companion_ai: ['read_personal_data', 'analyze_narrative'],
77
+ integration_ai: ['read_public_api'],
78
+ };
79
+ function isRole(value) {
80
+ return typeof value === 'string' && exports.ROLES.includes(value);
81
+ }
82
+ function hasPermission(role, permission) {
83
+ const perms = exports.ROLE_PERMISSIONS[role];
84
+ return perms.includes('*') || perms.includes(permission);
85
+ }
86
+ function hasRoleLevel(role, required) {
87
+ return exports.ROLE_HIERARCHY[role] >= exports.ROLE_HIERARCHY[required];
88
+ }
89
+ function rolesAtOrAbove(role) {
90
+ const level = exports.ROLE_HIERARCHY[role];
91
+ return exports.ROLES.filter((r) => exports.ROLE_HIERARCHY[r] >= level);
92
+ }
93
+ //# sourceMappingURL=roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.js","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAyEH,wBAEC;AAED,sCAGC;AAED,oCAEC;AAED,wCAGC;AAvFY,QAAA,KAAK,GAAG;IACnB,aAAa;IACb,gBAAgB;IAChB,sBAAsB;IACtB,YAAY;IACZ,cAAc;IACd,OAAO;IACP,cAAc;IACd,gBAAgB;CACR,CAAC;AAIE,QAAA,WAAW,GAAG;IACzB,cAAc;IACd,cAAc;IACd,SAAS;IACT,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,qBAAqB;IACrB,UAAU;IACV,yBAAyB;IACzB,sBAAsB;IACtB,eAAe;IACf,gBAAgB;IAChB,oBAAoB;IACpB,mBAAmB;IACnB,iBAAiB;IACjB,eAAe;CACP,CAAC;AAIX,4DAA4D;AAC/C,QAAA,cAAc,GAAyB;IAClD,WAAW,EAAE,CAAC;IACd,cAAc,EAAE,CAAC;IACjB,oBAAoB,EAAE,CAAC;IACvB,UAAU,EAAE,CAAC;IACb,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,CAAC;IACR,YAAY,EAAE,CAAC;IACf,cAAc,EAAE,CAAC;CAClB,CAAC;AAEW,QAAA,WAAW,GAAyB;IAC/C,WAAW,EAAE,aAAa;IAC1B,cAAc,EAAE,gBAAgB;IAChC,oBAAoB,EAAE,sBAAsB;IAC5C,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,KAAK,EAAE,OAAO;IACd,YAAY,EAAE,cAAc;IAC5B,cAAc,EAAE,gBAAgB;CACjC,CAAC;AAEF,MAAM,IAAI,GAAiB,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;AAC5D,MAAM,KAAK,GAAiB,CAAC,GAAG,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;AAEpD,QAAA,gBAAgB,GAAgD;IAC3E,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,KAAK;IACrB,oBAAoB,EAAE,CAAC,GAAG,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,qBAAqB,CAAC;IAC3F,UAAU,EAAE,CAAC,GAAG,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,yBAAyB,CAAC;IACjG,YAAY,EAAE,CAAC,GAAG,KAAK,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,CAAC;IAC9G,KAAK,EAAE,CAAC,GAAG,CAAC;IACZ,YAAY,EAAE,CAAC,oBAAoB,EAAE,mBAAmB,CAAC;IACzD,cAAc,EAAE,CAAC,iBAAiB,CAAC;CACpC,CAAC;AAEF,SAAgB,MAAM,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,aAA2B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnF,CAAC;AAED,SAAgB,aAAa,CAAC,IAAU,EAAE,UAAsB;IAC9D,MAAM,KAAK,GAAG,wBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC3D,CAAC;AAED,SAAgB,YAAY,CAAC,IAAU,EAAE,QAAc;IACrD,OAAO,sBAAc,CAAC,IAAI,CAAC,IAAI,sBAAc,CAAC,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED,SAAgB,cAAc,CAAC,IAAU;IACvC,MAAM,KAAK,GAAG,sBAAc,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,aAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAc,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AACzD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@medicine-wheel/community-identity",
3
+ "version": "0.14.0",
4
+ "description": "Who is in the circle: people as wheel nodes, roles with a permission map, circles and memberships as relations, invitations and self-issued credentials kept by the consumer — so a Medicine Wheel can tell two people apart.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "sideEffects": false,
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "dev": "tsc --watch",
23
+ "lint": "tsc --noEmit",
24
+ "clean": "rm -rf dist",
25
+ "prepublishOnly": "npm run clean && npm run build"
26
+ },
27
+ "keywords": [
28
+ "medicine-wheel",
29
+ "identity",
30
+ "roles",
31
+ "circles",
32
+ "talking-circle",
33
+ "invitations",
34
+ "relational-accountability"
35
+ ],
36
+ "author": "IAIP Collaborative - Shawinigan, QC",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/jgwill/medicine-wheel.git",
41
+ "directory": "src/community-identity"
42
+ },
43
+ "dependencies": {
44
+ "@medicine-wheel/ontology-core": "^0.14.0",
45
+ "zod": "^3.23.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.10.0",
49
+ "typescript": "^5.3.0"
50
+ }
51
+ }