@kubun/hub 0.14.0 → 0.15.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.
@@ -1,6 +1,7 @@
1
1
  import type { Identity } from '@kokuin/token';
2
2
  import type { Adapter } from '@kubun/db-adapter';
3
3
  import { type AdapterInput } from '@kubun/db-provider';
4
+ import type { AuthorizeHook } from '@kumiai/hub-server';
4
5
  import { Kysely } from 'kysely';
5
6
  import { type HubDatabase, type KubunHubStore } from './hub-store.js';
6
7
  import { type RelayObservers } from './observers.js';
@@ -20,6 +21,13 @@ export type CreateRelayOptions = {
20
21
  db: AdapterInput;
21
22
  identity: Identity;
22
23
  auth?: HubAuthConfig;
24
+ /**
25
+ * Per-action authorization hook, forwarded verbatim to `@kumiai/hub-server`
26
+ * `createHub`. Composes with `auth`: the coarse `AccessRules` gate runs first,
27
+ * then this refines per action and topic. Omitted, the hub allows any
28
+ * authenticated request (its default).
29
+ */
30
+ authorize?: AuthorizeHook;
23
31
  allowedOrigin?: string | Array<string>;
24
32
  retention?: RelayRetentionConfig;
25
33
  defaultRetention?: number;
@@ -38,6 +46,13 @@ export type RelayInternalOptions = {
38
46
  adapter: Adapter;
39
47
  identity: Identity;
40
48
  auth?: HubAuthConfig;
49
+ /**
50
+ * Per-action authorization hook, forwarded verbatim to `@kumiai/hub-server`
51
+ * `createHub`. Composes with `auth`: the coarse `AccessRules` gate runs first,
52
+ * then this refines per action and topic. Omitted, the hub allows any
53
+ * authenticated request (its default).
54
+ */
55
+ authorize?: AuthorizeHook;
41
56
  allowedOrigin?: string | Array<string>;
42
57
  retention?: RelayRetentionConfig;
43
58
  defaultRetention?: number;
@@ -97,6 +97,7 @@ export async function createRelayInternal(options) {
97
97
  store: hubStore,
98
98
  identity: options.identity,
99
99
  accessRules,
100
+ authorize: options.authorize,
100
101
  purge: false
101
102
  });
102
103
  if (observers?.onAuthRejected != null) {
@@ -222,6 +223,7 @@ export async function createRelay(options) {
222
223
  adapter,
223
224
  identity: options.identity,
224
225
  auth: options.auth,
226
+ authorize: options.authorize,
225
227
  allowedOrigin: options.allowedOrigin,
226
228
  retention: options.retention,
227
229
  defaultRetention: options.defaultRetention,
package/lib/index.d.ts CHANGED
@@ -4,8 +4,10 @@ import type { Kysely } from 'kysely';
4
4
  import { type HubAuthConfig, type RelayRetentionConfig } from './create-relay.js';
5
5
  import type { HubDatabase } from './hub-store.js';
6
6
  import type { RelayObservers } from './observers.js';
7
+ export type { AuthorizeDecision, AuthorizeHook, AuthorizeRequest } from '@kumiai/hub-server';
7
8
  export { type CreateRelayOptions, createRelay, createRelayInternal, type HubAuthConfig, type Relay, type RelayRetentionConfig, } from './create-relay.js';
8
9
  export { type CreateHubStoreParams, createHubStore, type HubDatabase, type KubunHubStore, type PurgedSubscription, type PurgeSubscriptionsParams, } from './hub-store.js';
10
+ export { type JoinLaneAuthorizeConfig, joinLaneAuthorize } from './join-lane-authorize.js';
9
11
  export { createHubMigrations, type MigrationContext } from './migrations.js';
10
12
  export { decorateHubStore, type RelayObserverAckInfo, type RelayObserverAuthRejectedInfo, type RelayObserverErrorInfo, type RelayObserverErrorStage, type RelayObserverFetchInfo, type RelayObserverPurgeInfo, type RelayObserverRetentionConfigClampedInfo, type RelayObserverStoreInfo, type RelayObserverSubscriptionPurgeInfo, type RelayObservers, } from './observers.js';
11
13
  export type HubOptions = {
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ import { Hono } from 'hono';
4
4
  import { createRelayInternal } from './create-relay.js';
5
5
  export { createRelay, createRelayInternal } from './create-relay.js';
6
6
  export { createHubStore } from './hub-store.js';
7
+ export { joinLaneAuthorize } from './join-lane-authorize.js';
7
8
  export { createHubMigrations } from './migrations.js';
8
9
  export { decorateHubStore } from './observers.js';
9
10
  export async function createHub(options) {
@@ -0,0 +1,13 @@
1
+ import type { AuthorizeHook } from '@kumiai/hub-server';
2
+ export type JoinLaneAuthorizeConfig = {
3
+ /** The single topic a not-yet-member device may touch. */
4
+ joinTopic: string;
5
+ /** Whether a DID is already a member (gets full access). */
6
+ isMember: (did: string) => boolean | Promise<boolean>;
7
+ };
8
+ /**
9
+ * Build an `authorize` hook implementing a pre-membership "join lane": a member
10
+ * DID may do anything; a non-member may only publish/subscribe/fetch exactly the
11
+ * one join topic, and nothing else. Pair with `createRelay({ authorize })`.
12
+ */
13
+ export declare function joinLaneAuthorize(config: JoinLaneAuthorizeConfig): AuthorizeHook;
@@ -0,0 +1,26 @@
1
+ // Actions a non-member is allowed on the join topic — the minimum to post a
2
+ // join request and receive the answer. Everything else is denied.
3
+ const JOIN_LANE_ACTIONS = new Set([
4
+ 'publish',
5
+ 'subscribe',
6
+ 'topic/fetch'
7
+ ]);
8
+ /**
9
+ * Build an `authorize` hook implementing a pre-membership "join lane": a member
10
+ * DID may do anything; a non-member may only publish/subscribe/fetch exactly the
11
+ * one join topic, and nothing else. Pair with `createRelay({ authorize })`.
12
+ */ export function joinLaneAuthorize(config) {
13
+ return async (req)=>{
14
+ if (await config.isMember(req.did)) {
15
+ return true;
16
+ }
17
+ const topicID = 'topicID' in req ? req.topicID : undefined;
18
+ if (JOIN_LANE_ACTIONS.has(req.action) && topicID === config.joinTopic) {
19
+ return true;
20
+ }
21
+ return {
22
+ allow: false,
23
+ reason: 'join lane: non-members may only use the join topic'
24
+ };
25
+ };
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/hub",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "license": "see LICENSE.md",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -18,26 +18,26 @@
18
18
  "@enkaku/server": "^0.21.2",
19
19
  "@hono/node-server": "^2.1.1",
20
20
  "@kokuin/token": "^0.5.0",
21
- "@kubun/db-adapter": "^0.14.0",
22
- "@kubun/db-provider": "^0.14.0",
23
- "@kubun/http-util": "^0.14.0",
24
- "@kumiai/hub-protocol": "^0.7.0",
25
- "@kumiai/hub-server": "^0.7.0",
21
+ "@kubun/db-adapter": "^0.15.0",
22
+ "@kubun/db-provider": "^0.15.0",
23
+ "@kubun/http-util": "^0.15.0",
24
+ "@kumiai/hub-protocol": "^0.8.0",
25
+ "@kumiai/hub-server": "^0.8.0",
26
26
  "@sozai/event": "^0.1.3",
27
27
  "get-port": "^7.2.0",
28
- "hono": "^4.13.4",
28
+ "hono": "^4.13.5",
29
29
  "kysely": "^0.29.5"
30
30
  },
31
31
  "devDependencies": {
32
- "@enkaku/client": "^0.21.0",
32
+ "@enkaku/client": "^0.21.1",
33
33
  "@enkaku/http-fetch": "^0.21.0",
34
34
  "@enkaku/protocol": "^0.21.0",
35
35
  "@enkaku/transport": "^0.21.0",
36
- "@kubun/db-better-sqlite": "^0.14.0",
37
- "@kubun/db-node-sqlite": "^0.14.0",
38
- "@kubun/db-postgres": "^0.14.0",
39
- "@kumiai/hub-conformance": "^0.7.0",
40
- "@kumiai/mls": "^0.7.0",
36
+ "@kubun/db-better-sqlite": "^0.15.0",
37
+ "@kubun/db-node-sqlite": "^0.15.0",
38
+ "@kubun/db-postgres": "^0.15.0",
39
+ "@kumiai/hub-conformance": "^0.8.0",
40
+ "@kumiai/mls": "^0.8.0",
41
41
  "@sozai/async": "^0.2.1",
42
42
  "@testcontainers/postgresql": "^12.1.0"
43
43
  },