@takosjp/yurucommu-core 3.0.2 → 3.2.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.en.md ADDED
@@ -0,0 +1,92 @@
1
+ 日本語: [README.md](README.md)
2
+
3
+ # Yurucommu Core
4
+
5
+ Yurucommu Core is the shared **ActivityPub / API / DB / runtime engine library**
6
+ for the yurucommu family. It is not the installable product repo and it does not
7
+ own an OpenTofu Capsule.
8
+
9
+ The deployable products are separate repositories:
10
+
11
+ - `yurucommu` owns the feed / story / profile fullstack product, `yurucommu.com`,
12
+ its Worker artifact, and its OpenTofu Capsule.
13
+ - `yurumeet` owns the talk-first fullstack product. `yurume` is the short client
14
+ id / abbreviation used in discovery and push registration.
15
+
16
+ ## Features
17
+
18
+ - **ActivityPub federation** — follow, post, boost, like, and reply across
19
+ servers to link your communities and connections, with HTTP-signature
20
+ verification, SSRF-guarded fetches, and actor/domain blocklists.
21
+ - **Server API** — actor, post, story, DM, community, media, notification, and
22
+ mobile-push routes.
23
+ - **Client contract** — a typed SDK and discovery types for yurucommu,
24
+ yurumeet, mobile, and alternate clients.
25
+ - **Search, notifications, recommendations** — discover people and content.
26
+ - **Auth** — password, Google / X OAuth, and Takosumi Accounts OIDC consumer.
27
+ - **Bilingual UI** — Japanese / English.
28
+
29
+ ## Deployment Boundary
30
+
31
+ Do not install this repo as a Capsule. Install `yurucommu` or `yurumeet`
32
+ instead. Those product repos bundle their own UI with this server engine, publish
33
+ their own Worker artifact, and expose their own plain OpenTofu module.
34
+
35
+ Client implementations should use `@takosjp/yurucommu-api`. Product Worker
36
+ artifacts use `@takosjp/yurucommu-core/server` to create the Hono backend and
37
+ `@takosjp/yurucommu-core/migrations` for D1 migration activation. They should
38
+ not import unpublished source paths from this checkout.
39
+
40
+ ## Develop
41
+
42
+ ```bash
43
+ cd yurucommu-core
44
+ bun install
45
+ bun run check # tsc --noEmit
46
+ bun test # bun:test suite
47
+ bun run lint # type check
48
+ bun run fmt # prettier
49
+ ```
50
+
51
+ The backend engine source lives in `src/backend` (Hono routes, ActivityPub
52
+ federation, delivery pipeline), the shared npm API package in `packages/api`,
53
+ and the database schema and migrations in `src/db/schema` and `migrations/`.
54
+
55
+ API package commands:
56
+
57
+ ```bash
58
+ bun run build:api # build @takosjp/yurucommu-api
59
+ bun run pack:api # npm pack --dry-run for the API package
60
+ ```
61
+
62
+ ### Release order for notification support in 3.1.0
63
+
64
+ The browser notification public API and `0019_notification_push_delivery.sql` become part of the core/API contract in
65
+ `3.1.0`. Publish both packages from the same `v3.1.0` tag and require the packed-consumer gate to pass first. Only after
66
+ that version is available from the npm registry may `yurucommu` and `yurumeet` update their dependency ranges and
67
+ `bun.lock` files from the registry. Each product must then pass `bun run check:core-release` before releasing its Worker.
68
+ Do not substitute unpublished source through a `file:`, `workspace:`, or Git dependency.
69
+
70
+ Yurumeet and mobile clients live in separate repositories. They should depend on
71
+ `@takosjp/yurucommu-api`, discover a server through Capsule outputs or
72
+ `/.well-known/social-server`, and deploy their own static/runtime artifact
73
+ through Takosumi, Cloudflare, or a self-host runtime. Local checkouts can be
74
+ placed under `clients/` for debugging, but that directory is intentionally
75
+ ignored so the server repo stays small.
76
+
77
+ When a client runs on a separate origin, the yurucommu-server must include that
78
+ origin in its CORS / CSRF allowlist.
79
+
80
+ ## Boundaries
81
+
82
+ Yurucommu implements its own ActivityPub federation, content distribution, and
83
+ user identity entirely at the app layer — it does not depend on Takos core
84
+ services or on a platform-layer federation mechanism. When installed through
85
+ Takosumi it is a normal Capsule a user can remove, and it is never absorbed into
86
+ Takos core. See [`AGENTS.md`](AGENTS.md) for the full product boundary.
87
+
88
+ ## Documentation
89
+
90
+ - [Deployment guide](https://yurucommu.com/help/deployment.html)
91
+ - [Getting started](https://yurucommu.com/help/getting-started.html)
92
+ - [Help site](https://yurucommu.com/help/)
package/README.md CHANGED
@@ -1,41 +1,44 @@
1
1
  # Yurucommu Core
2
2
 
3
- Yurucommu Core is the shared **ActivityPub / API / DB / runtime engine library**
4
- for the yurucommu family. It is not the installable product repo and it does not
5
- own an OpenTofu Capsule.
3
+ English: [README.en.md](README.en.md)
6
4
 
7
- The deployable products are separate repositories:
5
+ Yurucommu Core は、yurucommu family の製品 (yurucommu / yurumeet など) が共通で使う
6
+ **ActivityPub / API / DB / runtime エンジンのライブラリ** です。分散 SNS の連合・サーバー API・
7
+ データベース層を 1 か所に実装しておくことで、各製品は UI に集中できます。
8
8
 
9
- - `yurucommu` owns the feed / story / profile fullstack product, `yurucommu.com`,
10
- its Worker artifact, and its OpenTofu Capsule.
11
- - `yurumeet` owns the talk-first fullstack product. `yurume` is the short client
12
- id / abbreviation used in discovery and push registration.
9
+ これはインストールできる製品の repo ではなく、OpenTofu Capsule (Git URL から取り込む
10
+ 1 つのアプリ/インフラ単位) も持ちません。deploy できる製品は別の repo にあります。
13
11
 
14
- ## Features
12
+ - `yurucommu` — フィード / ストーリー / プロフィールの fullstack product。`yurucommu.com`・
13
+ Worker artifact・OpenTofu Capsule を持ちます
14
+ - `yurumeet` — トーク中心の fullstack product。`yurume` は discovery と push 登録で使う
15
+ 短い client id / 略称です
15
16
 
16
- - **ActivityPub federation** — follow, post, boost, like, and reply across
17
- servers to link your communities and connections, with HTTP-signature
18
- verification, SSRF-guarded fetches, and actor/domain blocklists.
19
- - **Server API** — actor, post, story, DM, community, media, notification, and
20
- mobile-push routes.
21
- - **Client contract** — a typed SDK and discovery types for yurucommu,
22
- Yurumeet, mobile, and alternate clients.
23
- - **Search, notifications, recommendations** — discover people and content.
24
- - **Auth** — password, Google / X OAuth, and Takosumi Accounts OIDC consumer.
25
- - **Bilingual UI** — Japanese / English.
17
+ ## できること
26
18
 
27
- ## Deployment Boundary
19
+ - **ActivityPub 連合** — フォロー・投稿・ブースト・いいね・返信をサーバーをまたいで行い、
20
+ コミュニティとつながりを結びます。HTTP-signature の検証、SSRF 対策済みの fetch、
21
+ actor / ドメインのブロックリストを備えます
22
+ - **サーバー API** — actor・投稿・ストーリー・DM・コミュニティ・メディア・通知・
23
+ モバイル push の route を提供します
24
+ - **クライアント契約** — yurucommu・yurumeet・モバイル・代替クライアント向けの型付き SDK と
25
+ discovery の型を提供します
26
+ - **検索・通知・レコメンド** — 人とコンテンツを見つけられます
27
+ - **認証** — パスワード、Google / X OAuth、Takosumi Accounts の OIDC クライアントに対応します
28
+ - **二言語 UI** — 日本語 / 英語に対応します
28
29
 
29
- Do not install this repo as a Capsule. Install `yurucommu` or `yurumeet`
30
- instead. Those product repos bundle their own UI with this server engine, publish
31
- their own Worker artifact, and expose their own plain OpenTofu module.
30
+ ## 使い方の境界
32
31
 
33
- Client implementations should use `@takosjp/yurucommu-api`. Product Worker
34
- artifacts use `@takosjp/yurucommu-core/server` to create the Hono backend and
35
- `@takosjp/yurucommu-core/migrations` for D1 migration activation. They should
36
- not import unpublished source paths from this checkout.
32
+ この repo Capsule としてインストールしないでください。代わりに `yurucommu` または
33
+ `yurumeet` をインストールします。これらの製品 repo が、自分の UI とこのサーバーエンジンを束ね、
34
+ 自分の Worker artifact を公開し、自分の plain OpenTofu module を持ちます。
37
35
 
38
- ## Develop
36
+ クライアント実装は `@takosjp/yurucommu-api` を使ってください。製品の Worker artifact は
37
+ `@takosjp/yurucommu-core/server` で Hono backend を作成し、
38
+ `@takosjp/yurucommu-core/migrations` で D1 migration を有効化します。この checkout の
39
+ 未公開 source path を import してはいけません。
40
+
41
+ ## 開発者向け
39
42
 
40
43
  ```bash
41
44
  cd yurucommu-core
@@ -46,36 +49,42 @@ bun run lint # type check
46
49
  bun run fmt # prettier
47
50
  ```
48
51
 
49
- The backend engine source lives in `src/backend` (Hono routes, ActivityPub
50
- federation, delivery pipeline), the shared npm API package in `packages/api`,
51
- and the database schema and migrations in `src/db/schema` and `migrations/`.
52
+ backend エンジンの source `src/backend` (Hono routes、ActivityPub 連合、配送 pipeline)、
53
+ 共有 npm API package `packages/api`、データベースの schema と migration は
54
+ `src/db/schema` `migrations/` にあります。
52
55
 
53
- API package commands:
56
+ API package のコマンド:
54
57
 
55
58
  ```bash
56
59
  bun run build:api # build @takosjp/yurucommu-api
57
60
  bun run pack:api # npm pack --dry-run for the API package
58
61
  ```
59
62
 
60
- Yurumeet and mobile clients live in separate repositories. They should depend on
61
- `@takosjp/yurucommu-api`, discover a server through Capsule outputs or
62
- `/.well-known/social-server`, and deploy their own static/runtime artifact
63
- through Takosumi, Cloudflare, or a self-host runtime. Local checkouts can be
64
- placed under `clients/` for debugging, but that directory is intentionally
65
- ignored so the server repo stays small.
63
+ ### 通知機能を含む 3.1.0 release 順序
64
+
65
+ ブラウザ通知の public API `0019_notification_push_delivery.sql` は core / API `3.1.0` からの契約です。
66
+ まずこの repo で両 package を同じ `v3.1.0` tag から npm に公開し、packaged consumer gate が通ることを確認します。
67
+ その公開が registry から取得できるようになった後にだけ、`yurucommu` / `yurumeet` dependency range
68
+ `bun.lock` registry package から更新し、各 repo `bun run check:core-release` を通してから製品版 Worker を
69
+ release します。`file:` / `workspace:` / Git dependency で未公開 source を代用してはいけません。
70
+
71
+ Yurumeet とモバイルクライアントは別 repo にあります。これらは `@takosjp/yurucommu-api` に依存し、
72
+ Capsule の output または `/.well-known/social-server` でサーバーを見つけ、自分の静的/runtime
73
+ artifact を Takosumi・Cloudflare・self-host の実行環境で deploy します。デバッグ用に
74
+ `clients/` の下へローカル checkout を置けますが、このディレクトリは意図的に git 管理外に
75
+ してあり、サーバー repo を小さく保ちます。
66
76
 
67
- When a client runs on a separate origin, the yurucommu-server must include that
68
- origin in its CORS / CSRF allowlist.
77
+ クライアントが別 origin で動く場合、yurucommu サーバー側の CORS / CSRF の許可リストに
78
+ その origin を含める必要があります。
69
79
 
70
- ## Boundaries
80
+ ## 製品としての境界
71
81
 
72
- Yurucommu implements its own ActivityPub federation, content distribution, and
73
- user identity entirely at the app layer — it does not depend on Takos core
74
- services or on a platform-layer federation mechanism. When installed through
75
- Takosumi it is a normal Capsule a user can remove, and it is never absorbed into
76
- Takos core. See [`AGENTS.md`](AGENTS.md) for the full product boundary.
82
+ Yurucommu ActivityPub 連合・コンテンツ配送・ユーザー identity をすべてアプリ層で自前実装して
83
+ います。Takos core のサービスにも、プラットフォーム層の連合の仕組みにも依存しません。Takosumi 経由で
84
+ インストールされた場合もユーザーが削除できる通常の Capsule であり、Takos core に取り込まれることは
85
+ ありません。詳しい境界は [`AGENTS.md`](AGENTS.md) を参照してください。
77
86
 
78
- ## Documentation
87
+ ## ドキュメント
79
88
 
80
89
  - [Deployment guide](https://yurucommu.com/help/deployment.html)
81
90
  - [Getting started](https://yurucommu.com/help/getting-started.html)
@@ -0,0 +1,100 @@
1
+ CREATE TABLE IF NOT EXISTS notification_pushers (
2
+ id TEXT PRIMARY KEY,
3
+ actor_ap_id TEXT NOT NULL,
4
+ product TEXT NOT NULL,
5
+ scope TEXT,
6
+ kind TEXT NOT NULL DEFAULT 'http',
7
+ app_id TEXT NOT NULL,
8
+ pushkey TEXT NOT NULL,
9
+ pushkey_hash TEXT NOT NULL,
10
+ app_display_name TEXT,
11
+ device_display_name TEXT,
12
+ profile_tag TEXT,
13
+ lang TEXT,
14
+ data_json TEXT NOT NULL DEFAULT '{}',
15
+ gateway_url TEXT NOT NULL,
16
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
17
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
18
+ last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
19
+ FOREIGN KEY (actor_ap_id) REFERENCES actors(ap_id) ON DELETE CASCADE
20
+ );
21
+
22
+ CREATE UNIQUE INDEX IF NOT EXISTS notification_pushers_actor_product_app_pushkey_idx
23
+ ON notification_pushers(actor_ap_id, product, app_id, pushkey_hash);
24
+
25
+ CREATE INDEX IF NOT EXISTS notification_pushers_actor_product_idx
26
+ ON notification_pushers(actor_ap_id, product);
27
+
28
+ CREATE UNIQUE INDEX IF NOT EXISTS notification_pushers_device_idx
29
+ ON notification_pushers(product, app_id, pushkey_hash);
30
+
31
+ CREATE INDEX IF NOT EXISTS notification_pushers_last_seen_idx
32
+ ON notification_pushers(last_seen_at);
33
+
34
+ CREATE TABLE IF NOT EXISTS notification_push_jobs (
35
+ id TEXT PRIMARY KEY,
36
+ actor_ap_id TEXT NOT NULL,
37
+ activity_ap_id TEXT NOT NULL,
38
+ product TEXT,
39
+ status TEXT NOT NULL DEFAULT 'pending',
40
+ processing_token TEXT,
41
+ attempts INTEGER NOT NULL DEFAULT 0,
42
+ pending_pusher_ids_json TEXT,
43
+ next_attempt_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
44
+ last_error TEXT,
45
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
46
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
47
+ delivered_at TEXT,
48
+ FOREIGN KEY (actor_ap_id) REFERENCES actors(ap_id) ON DELETE CASCADE
49
+ );
50
+
51
+ CREATE UNIQUE INDEX IF NOT EXISTS notification_push_jobs_actor_activity_idx
52
+ ON notification_push_jobs(actor_ap_id, activity_ap_id);
53
+
54
+ CREATE INDEX IF NOT EXISTS notification_push_jobs_status_next_idx
55
+ ON notification_push_jobs(status, next_attempt_at);
56
+
57
+ CREATE INDEX IF NOT EXISTS notification_push_jobs_terminal_retention_idx
58
+ ON notification_push_jobs(status, updated_at);
59
+
60
+ CREATE INDEX IF NOT EXISTS notification_push_jobs_actor_idx
61
+ ON notification_push_jobs(actor_ap_id);
62
+
63
+ -- Durable notification outbox: every unread inbox insert gets exactly one job.
64
+ -- The deterministic id contains only row identifiers and is never exposed to a
65
+ -- client. Delivery Queue messages carry this id, not pushkeys or content.
66
+ CREATE TRIGGER IF NOT EXISTS notification_push_jobs_after_inbox_insert
67
+ AFTER INSERT ON inbox
68
+ WHEN NEW.read = 0
69
+ BEGIN
70
+ INSERT OR IGNORE INTO notification_push_jobs (
71
+ id,
72
+ actor_ap_id,
73
+ activity_ap_id,
74
+ product,
75
+ status,
76
+ attempts,
77
+ next_attempt_at,
78
+ created_at,
79
+ updated_at
80
+ ) VALUES (
81
+ NEW.actor_ap_id || char(10) || NEW.activity_ap_id,
82
+ NEW.actor_ap_id,
83
+ NEW.activity_ap_id,
84
+ NULL,
85
+ 'pending',
86
+ 0,
87
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
88
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
89
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
90
+ );
91
+ END;
92
+
93
+ CREATE TRIGGER IF NOT EXISTS notification_push_jobs_after_inbox_delete
94
+ AFTER DELETE ON inbox
95
+ BEGIN
96
+ DELETE FROM notification_push_jobs
97
+ WHERE actor_ap_id = OLD.actor_ap_id
98
+ AND activity_ap_id = OLD.activity_ap_id
99
+ AND status IN ('pending', 'queued', 'retry_wait');
100
+ END;
@@ -69,31 +69,21 @@ file name (not the numeric version) in `yurucommu_migrations`.
69
69
  ```
70
70
 
71
71
  Remote Cloudflare D1 rejects explicit `BEGIN` / `SAVEPOINT` statements through
72
- this API, so the Takosumi release path disables transaction wrappers while
72
+ this API, so the Takosumi lifecycle-action path disables transaction wrappers while
73
73
  keeping the local/libSQL path wrapped by default.
74
74
 
75
75
  4. **Post-apply activation**:
76
76
 
77
- In the OpenTofu-managed Worker path, the product repo's
78
- `takosumi_release.post_apply` runs
79
- `bun run takosumi:release -- --migrations-only` as an opaque runner command.
77
+ In the OpenTofu-managed Worker path, the operator stores a versioned
78
+ `post_apply` lifecycle action in Takosumi's service-side InstallConfig. It
79
+ runs `bun run takosumi:release -- --migrations-only` as an opaque runner
80
+ command; no OpenTofu Output declares or selects it.
80
81
  It reads non-secret outputs from `TAKOSUMI_OUTPUTS_JSON`, writes a temporary
81
82
  Wrangler config, and applies these core migrations through
82
83
  `wrangler d1 execute` without explicit SQL transaction wrappers. Provider
83
84
  credentials come from the same reviewed Provider Connection used by the
84
85
  OpenTofu run.
85
86
 
86
- In the fallback path where the Worker script is not managed by OpenTofu,
87
- `takosumi_release.post_apply` runs `bun run takosumi:release` as an opaque
88
- operator release command. It reads non-secret outputs from `TAKOSUMI_OUTPUTS_JSON`,
89
- writes a temporary Wrangler config, runs `bun install --frozen-lockfile`,
90
- runs `bun run build:takos-worker`, applies D1 migrations through
91
- `wrangler d1 execute` without explicit SQL transaction wrappers, and deploys
92
- with `wrangler deploy`.
93
- Provider credentials and app-specific secrets come from the selected
94
- operator release activator boundary through
95
- `TAKOSUMI_RELEASE_COMMAND_ENV_ALLOWLIST`.
96
-
97
87
  Common operator env names:
98
88
 
99
89
  ```bash
@@ -112,8 +102,8 @@ file name (not the numeric version) in `yurucommu_migrations`.
112
102
  ## Product-local ledger note
113
103
 
114
104
  The `yurucommu_migrations` ledger is yurucommu-family product state. The
115
- Takosumi-managed path invokes product-owned `takosumi_release.post_apply`
116
- commands and records activation status/logs; it does not expose a Takosumi DB
105
+ Takosumi-managed path invokes product-owned service-side lifecycle actions and
106
+ records activation status/logs; it does not expose a Takosumi DB
117
107
  migration API. A future core migration may add `checksum TEXT` and store
118
108
  `sha256:<hex>` per applied migration, but that requires coordination with
119
109
  production data in both yurucommu and Yurumeet installations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takosjp/yurucommu-core",
3
- "version": "3.0.2",
3
+ "version": "3.2.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -23,6 +23,7 @@
23
23
  "packages/api/src/lib/api/account.ts",
24
24
  "packages/api/src/lib/api/actors.ts",
25
25
  "packages/api/src/lib/api/auth.ts",
26
+ "packages/api/src/lib/api/browser-push.ts",
26
27
  "packages/api/src/lib/api/communities.ts",
27
28
  "packages/api/src/lib/api/dm.ts",
28
29
  "packages/api/src/lib/api/fetch.ts",
@@ -56,17 +57,19 @@
56
57
  "dev": "bun src/backend/server.ts",
57
58
  "dev:server": "bun src/backend/server.ts",
58
59
  "check": "tsc --noEmit",
59
- "test": "bun run build:api && bun test test/ src/backend/ packages/api/src/",
60
+ "test": "bun run build:api && bun test test/ src/backend/ packages/api/src/ scripts/check-publish-version-discipline.test.ts scripts/publish-package-resumable.test.ts && bun run check:release-contents",
60
61
  "test:backend": "bun test src/backend/",
61
62
  "build": "bun run build:api",
62
63
  "build:api": "cd packages/api && bun run build",
64
+ "check:release-contents": "bun scripts/check-release-contents.mjs",
65
+ "check:packed-consumer": "bun scripts/check-packed-consumer.mjs",
63
66
  "prepublishOnly": "bun scripts/check-publish-version-discipline.mjs .",
64
67
  "pack:core": "npm pack --dry-run",
65
68
  "pack:api": "cd packages/api && bun run pack:dry",
66
69
  "app:activate": "bun scripts/apply-takosumi-migrations.ts",
67
70
  "lint": "bun run check",
68
- "fmt": "bunx prettier --write src test packages/api",
69
- "fmt:check": "bunx prettier --check src test packages/api",
71
+ "fmt": "bunx prettier --write src test packages/api scripts",
72
+ "fmt:check": "bunx prettier --check src test packages/api scripts",
70
73
  "db:generate": "bunx drizzle-kit generate",
71
74
  "db:push": "bunx drizzle-kit push",
72
75
  "db:migrate": "bunx drizzle-kit migrate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takosjp/yurucommu-api",
3
- "version": "3.0.1",
3
+ "version": "3.2.0",
4
4
  "description": "Typed client SDK and public API contract for yurucommu-server clients.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",