@h-ai/kit 0.1.0-alpha9 → 1.0.0-beta.2
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 +430 -0
- package/README.ja.md +430 -0
- package/README.md +351 -40
- package/dist/adapter/index.d.ts +17 -2
- package/dist/adapter/index.js.map +1 -1
- package/dist/browser.d.ts +19 -8
- package/dist/browser.js +10 -7
- package/dist/browser.js.map +1 -1
- package/dist/chunk-3OHUSKHI.js +175 -0
- package/dist/chunk-3OHUSKHI.js.map +1 -0
- package/dist/chunk-5VWPH7DO.js +126 -0
- package/dist/chunk-5VWPH7DO.js.map +1 -0
- package/dist/chunk-7EH47MTW.js +141 -0
- package/dist/chunk-7EH47MTW.js.map +1 -0
- package/dist/chunk-JRQPMPQC.js +63 -0
- package/dist/chunk-JRQPMPQC.js.map +1 -0
- package/dist/chunk-USFJN7IO.js +187 -0
- package/dist/chunk-USFJN7IO.js.map +1 -0
- package/dist/chunk-YKJRQCWR.js +363 -0
- package/dist/chunk-YKJRQCWR.js.map +1 -0
- package/dist/client/index.d.ts +51 -2
- package/dist/client/index.js +21 -2
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +303 -409
- package/dist/index.js +598 -310
- package/dist/index.js.map +1 -1
- package/dist/kit-a2a-types-4dapztu2.d.ts +136 -0
- package/dist/kit-client-auth-BnhrkzZF.d.ts +422 -0
- package/dist/kit-crypto-types-CXr888wf.d.ts +200 -0
- package/dist/kit-i18n-EBzZKYN-.d.ts +502 -0
- package/dist/kit-types-BkNdyKj8.d.ts +822 -0
- package/dist/modules/a2a/index.d.ts +15 -113
- package/dist/modules/a2a/index.js +2 -55
- package/dist/modules/a2a/index.js.map +1 -1
- package/dist/modules/crypto/index.d.ts +31 -79
- package/dist/modules/crypto/index.js +3 -3
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +36 -22
- package/dist/chunk-475WECWK.js +0 -110
- package/dist/chunk-475WECWK.js.map +0 -1
- package/dist/chunk-6TMSXG7J.js +0 -169
- package/dist/chunk-6TMSXG7J.js.map +0 -1
- package/dist/chunk-AD6S473Y.js +0 -137
- package/dist/chunk-AD6S473Y.js.map +0 -1
- package/dist/chunk-FRYJMRTP.js +0 -71
- package/dist/chunk-FRYJMRTP.js.map +0 -1
- package/dist/chunk-PHS7ZD5L.js +0 -274
- package/dist/chunk-PHS7ZD5L.js.map +0 -1
- package/dist/index-BotXLTck.d.ts +0 -203
- package/dist/kit-crypto-types-C-gEvx04.d.ts +0 -253
- package/dist/kit-i18n-CgpNTh9G.d.ts +0 -206
- package/dist/kit-types-BHlDNNpd.d.ts +0 -618
package/README.ja.md
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# @h-ai/kit
|
|
2
|
+
|
|
3
|
+
[简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
[フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/core](../core/README.ja.md) · [@h-ai/crypto](../crypto/README.ja.md) · [@h-ai/iam](../iam/README.ja.md)
|
|
6
|
+
|
|
7
|
+
## 概要
|
|
8
|
+
|
|
9
|
+
`@h-ai/kit` は hai-framework の SvelteKit 統合モジュールで、共通のリクエストパイプラインと同一オリジンの endpoint ツールを提供します:
|
|
10
|
+
|
|
11
|
+
- `kit.createHandle()`:セッション解析、ルートガード、middleware チェーン、任意の通信・Cookie 暗号化。
|
|
12
|
+
- `kit.handler()`:API endpoint の例外処理境界。
|
|
13
|
+
- `kit.guard` / `kit.response` / `kit.validate` / `kit.auth` / `kit.client` / `kit.crud`
|
|
14
|
+
|
|
15
|
+
> クロスプラットフォームの公開 HTTP contract は `@h-ai/api-contract` で定義し、`@h-ai/serv` にマウント、`@h-ai/api-client` の型付きクライアントで呼び出します。`@h-ai/kit` は業務 API contract を定義・提供しません。
|
|
16
|
+
|
|
17
|
+
## 主な機能
|
|
18
|
+
|
|
19
|
+
- Handle Hook の合成(`createHandle` / `sequence`)
|
|
20
|
+
- 認証・ロール・権限ガード(`guard.require` / `guard.check`)
|
|
21
|
+
- 組み込み Logging / RateLimit / CORS middleware(`createHandle` 設定で有効化)
|
|
22
|
+
- 共通の API 応答とリクエスト検証(Zod)
|
|
23
|
+
- Bearer Token ツール(サーバー login/logout + ブラウザーの Token 保存)
|
|
24
|
+
- 自動 CSRF と通信暗号化に対応するブラウザークライアント
|
|
25
|
+
- 宣言的な CRUD リソース定義(`crud.define`)
|
|
26
|
+
- A2A 統合:既定の検出パスは `/.well-known/agent-card.json`。`authenticate` 設定時は null/undefined が 401 となり、認証未設定時のみ匿名を許可します。API Key の引数は完全な Agent Card の `securitySchemes.apiKey` から取得します。Hook と明示的 A2A ルートは JSON-RPC 解析エラー・SSE キャンセル時の解放処理を共有します。
|
|
27
|
+
- 2 種類のビルドモード(`createAdapter()`)
|
|
28
|
+
|
|
29
|
+
## 使い始める
|
|
30
|
+
|
|
31
|
+
`sequence` は SvelteKit の resolve オプションを保持します。ページ変換は内側から外側へ実行し、preload とシリアライズ対象ヘッダーのフィルターは最初の設定を優先します。
|
|
32
|
+
|
|
33
|
+
### インストール
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.7
|
|
37
|
+
npm install svelte @sveltejs/kit zod
|
|
38
|
+
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 依存関係
|
|
42
|
+
|
|
43
|
+
- `@h-ai/iam`:A2A 用の認証・認可(任意)。A2A 機能を使う前に初期化。
|
|
44
|
+
- `@h-ai/crypto`:通信・Cookie 暗号化(任意)。暗号化を使う前に初期化。
|
|
45
|
+
|
|
46
|
+
### クイックスタート
|
|
47
|
+
|
|
48
|
+
#### プロジェクト設定
|
|
49
|
+
|
|
50
|
+
**svelte.config.js**:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import adapter from '@sveltejs/adapter-node'
|
|
54
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
55
|
+
|
|
56
|
+
const config = {
|
|
57
|
+
preprocess: [vitePreprocess()],
|
|
58
|
+
compilerOptions: { runes: true },
|
|
59
|
+
kit: {
|
|
60
|
+
adapter: adapter(),
|
|
61
|
+
// 双构建模式:import { createAdapter } from '@h-ai/kit/adapter'
|
|
62
|
+
// adapter: await createAdapter(),
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default config
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**vite.config.ts**:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { sveltekit } from '@sveltejs/kit/vite'
|
|
73
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
74
|
+
import { defineConfig } from 'vite'
|
|
75
|
+
|
|
76
|
+
export default defineConfig({
|
|
77
|
+
plugins: [sveltekit(), tailwindcss()],
|
|
78
|
+
optimizeDeps: { exclude: ['bits-ui'] },
|
|
79
|
+
ssr: { noExternal: ['@h-ai/ui', '@h-ai/kit'], external: ['@h-ai/core', 'sm-crypto'] },
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Crypto または Kit/IAM を使う場合、アプリの本番依存に `sm-crypto@0.5.7` を含めます。CLI 生成済みのプロジェクトでは設定済みです。手動導入は `pnpm add sm-crypto@0.5.7` を実行してください。adapter-node は直接の本番依存も参照するため、external 指定だけでは不十分です。
|
|
84
|
+
|
|
85
|
+
#### リクエストパイプラインの構築
|
|
86
|
+
|
|
87
|
+
`/yyy/`などの配下に配置する場合、SvelteKitの`kit.paths.base`とガードが参照する実際のURLを揃えます。`$app/paths`の`base`を`protectedPaths`、`publicPaths`、`loginUrl`に付け、`${base}/api/*`のように指定してください。認証画面は``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``を使用します。Kitはルートグループや配置接頭辞を含むアプリ内`/api/*`を識別し、未認証APIは401、画面は設定したログイン先へ遷移します。生成テンプレートにはこの接続を含みます。
|
|
88
|
+
|
|
89
|
+
以下の Hook 例では、アプリがデータベース、共有キャッシュ、IAM を初期化し、各結果を確認済みとします。リソースの終了はアプリが管理し、リクエストごとに初期化しません。
|
|
90
|
+
|
|
91
|
+
**src/hooks.server.ts**:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import type { Handle } from '@sveltejs/kit'
|
|
95
|
+
import { cache } from '@h-ai/cache'
|
|
96
|
+
import { iam } from '@h-ai/iam'
|
|
97
|
+
import { kit } from '@h-ai/kit'
|
|
98
|
+
|
|
99
|
+
const haiHandle = kit.createHandle({
|
|
100
|
+
auth: {
|
|
101
|
+
verifyToken: async (token) => {
|
|
102
|
+
const verified = await iam.auth.verifyToken(token)
|
|
103
|
+
return verified.success ? verified.data : null
|
|
104
|
+
},
|
|
105
|
+
loginUrl: '/auth/login',
|
|
106
|
+
protectedPaths: ['/admin/*', '/api/*'],
|
|
107
|
+
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
108
|
+
},
|
|
109
|
+
rateLimit: { windowMs: 60_000, maxRequests: 100, store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' }) },
|
|
110
|
+
logging: true,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
export const handle: Handle = haiHandle
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
最初の確定時点から固定ウィンドウを開始し、後続リクエストはTTLを延長しません。storeは必須です。共有Redisの初期化成功を確認し、全ノードのnamespace/windowMs/maxRequestsを一致させます。キャッシュ障害や競合上限は503、超過は429で、get+setやローカル計数へフォールバックしません。createMemoryRateLimitStoreは開発または明示HAI_E2E/HAI_E2E_MOCKテスト用で、常駐タイマーを使わず参照解放で回収されます。1リクエスト最大64項目を遅延走査し、上限10000ウィンドウです。満杯時は有効な計数を追い出さず新キーを503にし、外部storeのライフサイクルは作成者が管理します。本番でテスト変数を設定しません。ヘッダー時刻は推定、実際の期間はRedis TTLで制御するため時計を同期します。キーは信頼済みadapterのgetClientAddressのみ使い、等価IPv6/IPv4表記を正規化します。生の転送ヘッダーを信頼せず、代理信頼はadapterで設定します。
|
|
117
|
+
|
|
118
|
+
#### API endpoint
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { kit } from '@h-ai/kit'
|
|
122
|
+
import { z } from 'zod'
|
|
123
|
+
|
|
124
|
+
const CreateUserSchema = z.object({
|
|
125
|
+
name: z.string().min(1),
|
|
126
|
+
email: z.string().email(),
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
130
|
+
kit.guard.require(locals.session, 'user:create')
|
|
131
|
+
const payload = await kit.validate.body(request, CreateUserSchema)
|
|
132
|
+
return kit.response.created(payload)
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### ブラウザークライアント
|
|
137
|
+
|
|
138
|
+
`kit.client.create()` は SvelteKit の同一オリジン endpoint 向けです。Web・アプリ・ミニプログラムからクロスオリジンで公開 API を使う場合は `@h-ai/api-client` を使用してください。
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// src/lib/utils/api.ts — 全应用共享
|
|
142
|
+
import { kit } from '@h-ai/kit'
|
|
143
|
+
|
|
144
|
+
const client = kit.client.create()
|
|
145
|
+
export const { apiFetch } = client
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// 使用
|
|
150
|
+
import { apiFetch } from '$lib/utils/api'
|
|
151
|
+
|
|
152
|
+
const response = await apiFetch('/api/users', { method: 'GET' })
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> CSRF Header は `apiFetch` が自動付与します。`@h-ai/kit` は独立した CSRF middleware ファクトリーを提供しません。
|
|
156
|
+
> `apiFetch` は文字列、URL、Request を受け付けます。Request のメソッド、ヘッダー、本文を保持し、書き込みには CSRF を付与します。transport 有効時は対応する本文のみ暗号化し、multipart の境界を保持します。
|
|
157
|
+
>
|
|
158
|
+
> 同一オリジンの SvelteKit endpoint は httpOnly Cookie を使い、サーバーで検証します。ブラウザーの Bearer Header が必要なら `kit.auth.setBrowserToken()` でページ内メモリーに保存してから `kit.client.create({ auth: true })` を使います。独自の `BrowserTokenStore` もメモリーまたはプラットフォームの安全なストレージを使用し、機密の認証情報を Web Storage に永続化しないでください。
|
|
159
|
+
|
|
160
|
+
#### 同一オリジンの通信暗号化
|
|
161
|
+
|
|
162
|
+
kit の通信暗号化は `@h-ai/crypto` に委譲します。サーバーは `crypto.transport.createServer({ serverKeyRing, keyStore })` で管理器を作成し、ブラウザーは `kit.client.installBrowserTransport(kitConfig, { crypto })` の 1 行で同一オリジンの fetch ラッパーを導入します。サーバーには管理済み鍵環と共有 registry も渡します。
|
|
163
|
+
|
|
164
|
+
サブディレクトリでは `$app/paths` の `base` を使用します。サーバーの `crypto.transport` に `basePath: base`、ブラウザーには `installBrowserTransport(kitConfig, { crypto, basePath: base })` を渡し、独立した `kit.client.create` の `transport.basePath` も同じ値にします。既定の空文字列はドメイン直下です。ホストのビルド時ディレクトリを使う実行時引数であり、YAML に重複して設定したり、転送ヘッダーから推測したりしません。鍵交換・除外・容量のパスは引き続き完全なリクエストパスです。CLI の SSR テンプレートは両側を設定します。
|
|
165
|
+
|
|
166
|
+
**src/hooks.server.ts**:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { base } from '$app/paths'
|
|
170
|
+
import { appKitConfig } from '$lib/server/kit-config'
|
|
171
|
+
import { cache } from '@h-ai/cache'
|
|
172
|
+
import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
173
|
+
import { iam } from '@h-ai/iam'
|
|
174
|
+
import { kit } from '@h-ai/kit'
|
|
175
|
+
|
|
176
|
+
const cryptoReady = await crypto.init()
|
|
177
|
+
if (!cryptoReady.success)
|
|
178
|
+
throw new Error(cryptoReady.error.message)
|
|
179
|
+
const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
|
|
180
|
+
if (!ring.success)
|
|
181
|
+
throw new Error(ring.error.message)
|
|
182
|
+
const serverKeyRing = ring.data
|
|
183
|
+
// Redis/IAM 由应用启动流程初始化并检查;此处复用同一实例。
|
|
184
|
+
|
|
185
|
+
export const handle = kit.createHandle({
|
|
186
|
+
auth: {
|
|
187
|
+
verifyToken: async (token) => {
|
|
188
|
+
const verified = await iam.auth.verifyToken(token)
|
|
189
|
+
return verified.success ? verified.data : null
|
|
190
|
+
},
|
|
191
|
+
protectedPaths: [`${base}/api/*`],
|
|
192
|
+
publicPaths: [`${base}/api/_hai/*`], // 默认密钥协商端点
|
|
193
|
+
},
|
|
194
|
+
crypto: {
|
|
195
|
+
crypto,
|
|
196
|
+
transport: appKitConfig.transport === false
|
|
197
|
+
? false
|
|
198
|
+
: {
|
|
199
|
+
...appKitConfig.transport,
|
|
200
|
+
basePath: base,
|
|
201
|
+
serverKeyRing,
|
|
202
|
+
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**src/routes/+layout.svelte**(推奨入口:ブラウザー側は 1 行):
|
|
209
|
+
|
|
210
|
+
```svelte
|
|
211
|
+
<script lang='ts'>
|
|
212
|
+
import { browser } from '$app/environment'
|
|
213
|
+
import { base } from '$app/paths'
|
|
214
|
+
import { page } from '$app/state'
|
|
215
|
+
import { crypto } from '@h-ai/crypto'
|
|
216
|
+
import { kit } from '@h-ai/kit'
|
|
217
|
+
import { untrack } from 'svelte'
|
|
218
|
+
|
|
219
|
+
// 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
|
|
220
|
+
if (browser) {
|
|
221
|
+
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
|
|
222
|
+
}
|
|
223
|
+
</script>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**src/lib/utils/api.ts**(業務層では `apiFetch` のみを使用):
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { kit } from '@h-ai/kit'
|
|
230
|
+
|
|
231
|
+
export const { apiFetch } = kit.client.create()
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> `src/hooks.client.ts` は不要です。既定の `handleFetch` で十分で、SvelteKit 内部の `__data.json` も導入済みのグローバル fetch ラッパーを自動使用します。同一オリジンの Authorization Header には明示的な `BrowserTokenStore` または `kit.auth.setBrowserToken()` + `kit.client.create({ auth: true })` を使い、機密 Token を `localStorage` に保存しないでください。
|
|
235
|
+
|
|
236
|
+
既定の鍵交換パスは `/api/_hai/key-exchange` です。サーバーで `transport.keyExchangePath` を変更した場合、クライアントの `transport.keyExchangeUrl` も合わせて設定してください。
|
|
237
|
+
|
|
238
|
+
サーバーには同じ管理済み `serverKeyRing` と共有 `keyStore`(Redis または共有 DB)を明示的に渡します。クライアント公開鍵の共有だけではノード間で復号できません。暗黙のプロセス鍵・メモリー registry はなく、開発・テストでは `createInMemoryKeyStore(capacity)` を明示します。再起動後は同じ鍵環を読み込み、sticky session は不要です。
|
|
239
|
+
|
|
240
|
+
transport は既定で同一オリジンの `/api/*`、SvelteKit の `__data.json`、鍵交換 endpoint に適用します。ページ文書・静的ファイルは平文で `X-Client-Id` は不要です。アップロードなどの `multipart/form-data` はそのまま送信し、サーバーの `excludePaths` で明示的に許可してください。
|
|
241
|
+
|
|
242
|
+
認証済み v2 のみを受け付けます。独立したランダム SM4-CBC 鍵と HMAC-SM3 鍵を SM2 で包み、送信者署名、MAC の順に検証してから CBC を復号します。要求はクライアント秘密鍵、応答は選択されたサーバー鍵バージョンで署名します。version、direction、serverKeyId、clientId、nonce を兼ねるランダム 128 ビット requestId、issuedAt、HTTP method、pathname と元の search の SM3 pathHash、応答 status を認証します。要求の有効期限は 5 分、未来方向の時計差は最大 30 秒です。共有 registry が (clientId, requestId) を原子的に消費し、ノード間の時計差を含め issuedAt + 6 分まで保持します。保存障害では拒否します。時計同期と Redis の noeviction/永続化により有効な nonce を保持してください。業務の再試行には別の冪等キーが必要です。
|
|
243
|
+
|
|
244
|
+
空の POST も空平文の認証封装を送ります。GET/HEAD は X-Hai-Transport ヘッダー、204/304/HEAD など本文なし応答も同じヘッダーを使います。他の要求・応答は JSON 本文と X-Encrypted: true を使います。クライアントは元の要求コンテキストと HTTP status を照合し、マーカー欠落、旧形式、認証失敗では reject します。平文 endpoint は Serv/Kit の `encryption: disabled` ルールまたはホストの `excludePaths` で明示的に許可し、呼出側も通常の要求を選びます。API Client/Kit のラッパーは自身の `excludePaths` を使用します。CORS では X-Hai-Transport を許可・公開してください。鍵交換の公開鍵認証は TLS に依存し、IAM の業務認可は別に必要です。
|
|
245
|
+
|
|
246
|
+
`transport.maxPlaintextBytes` でリクエストとレスポンスの JSON 平文容量を共通設定します。単位は UTF-8 バイト、既定値は 1 MiB、有効範囲は 1 KiB~64 MiB の整数です。クライアントは鍵交換で上限を取得し、`resolveTransportBodyLimits()` が暗号文とプロキシ容量(平文の 2 倍 + 32 KiB)を算出します。鍵交換、公開鍵、認証ヘッダー、防リプレイ制約は独立です。超過時は元ストリームを停止して reader を解放します。要求は 413、応答は暗号化された 500 容量エラーとなり、`TransportBodyTooLargeError` / `hai:crypto:070` で識別できます。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
|
|
247
|
+
|
|
248
|
+
通常業務は `transport.maxPlaintextBytes`(auto/省略は 1 MiB)を維持します。Feedback の受理・証拠・添付・ZIP ダウンロードは `routePlaintextBytes` と `feedback.budget.maxTransportBytes` で別途宣言します。`*` は完全な1パス区間のみ一致し、ホストの明示値が優先です。ハンドシェイク、adapter-node、プロキシは経路の最大値を使用します。HTTP アダプターは個別宣言された 1 MiB 超の資料経路を最大2件、通常業務を別枠で16件まで許可し、応答完了・失敗時に解放します。混雑は503 / `hai:crypto:072` を返し、業務書込は自動再送しません。
|
|
249
|
+
|
|
250
|
+
serv/kit は要求の結合、署名、MAC を検証した後、時間窓を超えた報文に暗号化した HTTP 400 時刻エラーを返します。クライアントは `TransportTimeError`(`hai:crypto:071`)で端末とサーバーの時刻同期・再読み込みを案内します。`api-client` は `VALIDATION_FAILED` に変換し `cause.transportCode` に原因を保持します。自動再送やリプレイ時間窓の緩和は行わず、改ざん・未認証の要求は従来の認証エラーです。
|
|
251
|
+
|
|
252
|
+
#### `_kit.yml` による共通 transport 設定
|
|
253
|
+
|
|
254
|
+
`@h-ai/kit` は `KitConfigSchema` / `resolveKitConfig()` を提供し、同じ `_kit.yml` から
|
|
255
|
+
`hooks.server.ts` とブラウザーの `kit.client.installBrowserTransport()` の両方を設定できます:
|
|
256
|
+
|
|
257
|
+
```yml
|
|
258
|
+
server:
|
|
259
|
+
host: 127.0.0.1
|
|
260
|
+
port: 3000
|
|
261
|
+
|
|
262
|
+
transport:
|
|
263
|
+
keyExchangePath: /api/_hai/key-exchange
|
|
264
|
+
excludePaths:
|
|
265
|
+
- /api/storage
|
|
266
|
+
- /api/public
|
|
267
|
+
- /api/auth/profile/avatar
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { core } from '@h-ai/core'
|
|
272
|
+
import { KitConfigSchema } from '@h-ai/kit'
|
|
273
|
+
|
|
274
|
+
// 仅放在 src/lib/server 中;浏览器通过 layout data 接收公共投影。
|
|
275
|
+
const loaded = core.config.load('kit', './config/_kit.yml', KitConfigSchema)
|
|
276
|
+
if (!loaded.success)
|
|
277
|
+
throw new Error(loaded.error.message)
|
|
278
|
+
export const appKitConfig = loaded.data
|
|
279
|
+
export const publicKitConfig = { transport: appKitConfig.transport }
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`src/routes/+layout.server.ts` から上記ブラウザーレイアウトへ公開部分だけ渡します。ブラウザー側からサーバー設定をインポートしません。
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { publicKitConfig } from '$lib/server/kit-config'
|
|
286
|
+
|
|
287
|
+
export function load() {
|
|
288
|
+
return { kitConfig: publicKitConfig }
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
`resolveKitConfig()` は `server`(host/port)と `transport` を解決します。host/port は `HOST` / `PORT` 環境変数 > `_kit.yml` の `server` > `127.0.0.1:3000` の順で、SvelteKit サーバー入口で共通利用します(`@h-ai/serv` と同じ構造)。
|
|
293
|
+
サーバーのみで設定を読む場合は `core.config.validate('kit', KitConfigSchema)` も利用できます。
|
|
294
|
+
完全な `_kit.yml` はサーバーだけで読みます。内部の代理ネットワークやポリシーファイルのパスを含むため、`+layout.server.ts` は `{ kitConfig: publicKitConfig }` のような公開投影だけを返し、ブラウザーは transport を使用します。`?raw` で設定全体やポリシーをブラウザーへ含めず、秘密鍵も記載しないでください。
|
|
295
|
+
|
|
296
|
+
### 設定
|
|
297
|
+
|
|
298
|
+
`kit.createHandle()` の設定項目:
|
|
299
|
+
|
|
300
|
+
| フィールド | 型 | 説明 |
|
|
301
|
+
| ------------ | ---------------------------------------- | --------------------------------------------------------------- |
|
|
302
|
+
| `auth` | `HandleAuthConfig` | 認証(verifyToken / protectedPaths / publicPaths / loginUrl) |
|
|
303
|
+
| `rateLimit` | `object \| false` | レート制限(windowMs / maxRequests) |
|
|
304
|
+
| `logging` | `boolean \| object` | リクエストログ(既定 `true`、`{ logBody: true }` で本文も記録) |
|
|
305
|
+
| `crypto` | `HookCryptoConfig` | 通信暗号化 + Cookie 暗号化 |
|
|
306
|
+
| `guards` | `GuardConfig[]` | 独自ガード(自動認証ガードの後に実行) |
|
|
307
|
+
| `middleware` | `Middleware[]` | 独自 middleware(組み込み middleware の後に実行) |
|
|
308
|
+
| `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A 統合(Agent Card + JSON-RPC) |
|
|
309
|
+
| `onError` | `function` | 独自のエラー処理 |
|
|
310
|
+
|
|
311
|
+
リクエスト/レスポンス概要は `trace` レベルで記録します。ログは既定で有効、`logBody` は既定で無効です。クエリーは元の `request.url`、パスは SvelteKit の現在の `event.url.pathname` を使用します。追跡付き `event.url.search/searchParams` を読まないため、ログを有効にした静的ページも事前生成できます。クエリーと任意の JSON 本文ログでは password/token/secret、認証、API キーなどを秘匿します。本文は複製から読み、アプリ側で引き続き利用できます。
|
|
312
|
+
|
|
313
|
+
リクエストの自動監査は `audit.isInitialized` の場合だけ実行します。静的サイトや Audit を選択しないアプリは handle のために初期化する必要がありません。各リクエストで確認するため、handle 作成後のモジュール初期化にも対応します。Audit を選択したアプリは `audit.init()` の結果を確認し、失敗時は起動を止めます。明示的な `audit.log/capture` の未初期化エラーは維持し、初期化済みモジュールの実際の書込み失敗は `Request audit failed` として記録します。必須の業務監査では明示的な結果を確認し、業務トランザクションの規則を適用してください。
|
|
314
|
+
|
|
315
|
+
`crypto.encryptedCookies` を設定する場合、`crypto.cookieEncryptionKey` または `HAI_KIT_COOKIE_KEY` が必要です。鍵がなければ `createHandle()` ですぐエラーになります。保護対象 Cookie の平文・破損暗号文・復号失敗は未存在として扱い、暗号化失敗時も平文保存に切り替えません。
|
|
316
|
+
|
|
317
|
+
認証 Cookie は `NODE_ENV` または `HAI_CORE_ENV` が `production` の場合に `Secure` を既定で有効にします。`HAI_E2E` / `HAI_E2E_MOCK` は Cookie の安全要件を緩和しません。本番のレート制限も両方の環境変数を認識し、明示的なテストフラグだけがメモリカウンターを許可します。
|
|
318
|
+
|
|
319
|
+
### エクスポートパス
|
|
320
|
+
|
|
321
|
+
実行時メソッドは `kit` 名前空間または明示したサブパスを使用し、型は個別に公開します。browser 宣言はサーバーファクトリを公開しません。Vite 設定には `@sveltejs/kit/vite` を使い、`@h-ai/kit/vite` プラグインはありません。
|
|
322
|
+
|
|
323
|
+
- `@h-ai/kit`:主入口。`kit` 名前空間と型を提供。
|
|
324
|
+
- `@h-ai/kit/client`:ブラウザークライアント。
|
|
325
|
+
- `@h-ai/kit/adapter`:2 モードのビルド adapter(`createAdapter()`)。
|
|
326
|
+
- `@h-ai/kit/a2a`:A2A 統合。
|
|
327
|
+
- `@h-ai/kit/crypto`:暗号モジュール統合。
|
|
328
|
+
|
|
329
|
+
### 設定による認証画面
|
|
330
|
+
|
|
331
|
+
パスワードログインの送信項目は `identifier` と `password` のみです。`AuthPanel` は `LoginForm` の `username` を `identifier` に変換します。有効期間はサーバーの `session.maxAge` / `session.refreshTokenMaxAge` で設定し、フォームにログインごとの永続セッション選択肢はありません。
|
|
332
|
+
|
|
333
|
+
規約は設定の userAgreementMarkdown/privacyPolicyMarkdown または URL を使用できます。同じ規約は Markdown 本文を優先して安全な Modal で描画します。
|
|
334
|
+
|
|
335
|
+
kit.auth.createApi(iam) の標準 handler と kit.auth.publicPaths を使用し、AuthPanel には kit.client.createAuthActions({ apiFetch }) を接続します。検証、Cookie、HaiResult 変換を共通化し、ユーザー情報と改密の保護を維持します。
|
|
336
|
+
|
|
337
|
+
[完整 IAM 配置 / IAM configuration](../iam/README.ja.md)
|
|
338
|
+
|
|
339
|
+
`kit.auth.logout(cookies, accessToken)` は `Promise<HaiResult<void>>` を返します。Token がなければ Cookie のみ消去して成功し、Token があるのに認証操作が未設定なら失敗します。サーバー結果にかかわらず Cookie は消去しますが、呼出側は失敗を HTTP 応答や UI に伝えます。ローカル消去はサーバー失効の確認ではありません。制御フロー例外は引き続き伝播します。
|
|
340
|
+
|
|
341
|
+
### エラー処理
|
|
342
|
+
|
|
343
|
+
- API endpoint は `kit.handler()` でラップすることを推奨。
|
|
344
|
+
- `kit.validate.body` / `kit.guard.require` の `Response` は `kit.handler` が API 制御フローとして処理します。ページ `load` では `kit.guard.check` と SvelteKit `error` を使い、検索条件は `safeParse` で検証してネイティブなページエラーに変換します。API Response をそのまま throw しません。
|
|
345
|
+
- 制御フロー以外の例外は `kit.handler()` が `500 INTERNAL_ERROR` 応答に変換。
|
|
346
|
+
|
|
347
|
+
### CRUD fields
|
|
348
|
+
|
|
349
|
+
宣言型 CRUD 項目は UI フォームの幅を指定する `colSpan: 'auto' | 1 | 2` に対応します。`validation.message` は文字列または `() => string` を受け取り、現在の言語で表示できます。レイアウトは `CrudPage.form` で設定し、サーバーでも検証してください。
|
|
350
|
+
|
|
351
|
+
### リクエスト言語の分離
|
|
352
|
+
|
|
353
|
+
Node リクエストと SSR は `core.i18n.runWithLocale(locale, () => handleRequest())` で囲みます。SvelteKit は `kit.i18n.runWithLocale(locale, () => resolve(event))` を使えます。serv はリクエストヘッダーから自動設定します。非同期の子処理も言語を継承し、明示的な `MessageOptions.locale`、リクエスト言語、起動時の既定値の順で優先します。コンテキスト内の `setGlobalLocale` はそのリクエストだけを変更します。コンテキスト外の設定は起動時に限定し、リクエスト hook から呼びません。
|
|
354
|
+
|
|
355
|
+
ブラウザーはアプリ全体の `setGlobalLocale` / `createLocaleStore` を使います。SSR の UI store 初期化と setter は自身の状態のみ変更し、メッセージはリクエストコンテキストを参照します。`runWithLocale` は Node 入口専用です。
|
|
356
|
+
|
|
357
|
+
## フィードバック入口とリクエスト関連付け
|
|
358
|
+
|
|
359
|
+
`kit.createFeedbackHandler` は `/api/feedback/[...path]/+server.ts` の GET/POST/PATCH に使用できます。serv と同じ api-contract schema を再利用し、入力ストリームの容量とクライアント認証情報の混入を制限します。resolveContext は検証済み locals.session から範囲・権限を作り、null は拒否します。既存 createHandle の認証・CSRF・通信暗号化を維持してください。
|
|
360
|
+
|
|
361
|
+
createHandle は有効な上限付き X-Request-Id を保持して応答に付け、非同期業務にリクエストと検証済みユーザーを伝播します。通常の業務ログを収集する場合、ホストの middleware で `core.context.run({ scopeId: 'configured-product' }, next)` により業務を包みます。範囲は feedback と一致させ、クライアントのヘッダーから取得しません。
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
import type { SessionData } from '@h-ai/kit'
|
|
365
|
+
import { feedback } from '@h-ai/feedback'
|
|
366
|
+
import { kit } from '@h-ai/kit'
|
|
367
|
+
|
|
368
|
+
const handler = kit.createFeedbackHandler({
|
|
369
|
+
feedback,
|
|
370
|
+
resolveContext: (event) => {
|
|
371
|
+
const session = (event.locals as { session?: SessionData }).session
|
|
372
|
+
return session
|
|
373
|
+
? { scopeId: 'configured-product', userId: session.userId, permissions: session.permissions }
|
|
374
|
+
: null
|
|
375
|
+
},
|
|
376
|
+
})
|
|
377
|
+
export const GET = handler
|
|
378
|
+
export const POST = handler
|
|
379
|
+
export const PATCH = handler
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## エラー表示とログの関連付け
|
|
383
|
+
|
|
384
|
+
HTTP エラーには安全な翻訳文と追跡番号を含めます。ToastContainer は番号を既定で非表示にし、調査情報のアイコンから表示・コピーできます。同じ ID を error.traceId と X-Trace-Id に返します。requestId を traceId として共有し、ブラウザーとサーバーのログを関連付けます。脱機密化した例外・スタック・操作情報はログに残し、内部 cause/ext は公開しません。項目検証は翻訳済みの項目名とメッセージだけを返します。
|
|
385
|
+
|
|
386
|
+
HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表示には core.error.formatMessage(error) を使い、ID を重複追加しません。ログには完全な Error を渡してください。DB ログにはドライバーコード、脱機密化したメッセージ・スタック、表・列を残し、SQL 本文、バインド値と接続認証情報を記録しません。
|
|
387
|
+
|
|
388
|
+
audit の初期化後、createHandle は検証済みセッションの身元/役割、プラットフォーム getClientAddress と要求 ID を業務 audit.log に関連付けます。request.execute はルート雛形・メソッド・状態・成功/失敗/拒否を記録し、守衛リダイレクトと SvelteKit HTTP 制御フローも含みます。要求本文・内部エラーは複製しません。capture.enabled で自動記録、capture.includeReads で成功読取を制御し、失敗した読取は既定で記録します。業務完了/失敗はアプリが明示的に記録し、自動記録を無効にしても業務 log は維持します。
|
|
389
|
+
|
|
390
|
+
## 任意の対話チャレンジ
|
|
391
|
+
|
|
392
|
+
kit.auth.createApi(iam).createCaptcha を POST /api/auth/captcha として公開します。kit.auth.publicPaths は対応済みです。kit.client.createAuthActions は captcha を保持し createCaptcha を提供し、AuthPanel が自動求解します。初回/期限切れ変更には /api/auth/password/renew も公開します。IAM がサーバーで証明を強制検証します。
|
|
393
|
+
|
|
394
|
+
方針・既定値・復旧範囲は次を参照: [IAM](../iam/README.ja.md)。
|
|
395
|
+
|
|
396
|
+
## 期限切れセッションの画面内復帰
|
|
397
|
+
|
|
398
|
+
`kit.client.create({ recoverSession })` によって現在の画面と入力を保持して再ログインできます。UI の `createSessionRecovery` / `SessionRecoveryDialog` を使い、ルートで公開方針と元の利用者を設定し、`recovery.recover` と標準認証 actions を渡して破棄時に cancel します。IAM は発行前に `expectedUserId` を確認し、通常の CAPTCHA・ロック・改密・アクセス規則を適用します。認証拒否が明確な要求だけ1回続行でき、取消・5xx・タイムアウト・結果不明の書込は再送しません。旧セッションの重要操作証明は再確認が必要な場合があります。標準認証 actions は自身の復帰再試行を無効にします。独自認証は apiFetch の第3引数 `{ recoverSession: false }` で再帰を防ぎます。terminalType(web/app/desktop/api、既定 web)は分類用で機器認証ではありません。
|
|
399
|
+
|
|
400
|
+
## ページ離脱時のセッション方針
|
|
401
|
+
|
|
402
|
+
`session.endOnPageHide` の既定値は `false` です。ホストは `actions.bindPageExit({ enabled: config.session.endOnPageHide })` を呼び、破棄時に戻り値の解除関数を実行します。標準 admin/H5/fullstack テンプレートは接続済みです。BFCache 以外の `pagehide`(再読み込み、文書全体の移動を含む)で POST `/auth/session/close` を最善努力で送信し、保持する access と関連 refresh を失効します。既存の CSRF/暗号化転送を使用し、`keepalive` を付け、自動更新しません。古い応答が新しいログインを上書きしないよう Cookie/ローカル資格情報を消去しません。明示的ログアウトは通常どおり失効と消去を行います。ブラウザーが離脱要求を送らない場合もあるため、業務に応じてサーバーの `idleTimeout`/`absoluteTimeout` を併用してください。閉じた瞬間の失効は保証できません。
|
|
403
|
+
|
|
404
|
+
## 転送容量とモジュール予算
|
|
405
|
+
|
|
406
|
+
設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは crypto.resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
|
|
407
|
+
|
|
408
|
+
重要操作では `reauthorize: () => sensitiveActions.authorize(operation)` を Kit apiFetch の第3引数、または typed client の options.context に渡します。セッション更新・復帰または明確な証明拒否の後、新しい用途証明で1回だけ続行します。取消時は追加の書込を送らず、コールバックがなければ旧証明を再送しません。
|
|
409
|
+
|
|
410
|
+
## リクエストごとのアクセス制御
|
|
411
|
+
|
|
412
|
+
パスルールの `encryption` は required / disabled、省略時はホスト設定です。disabled でもアクセス条件と認証は検証し、フレームワークの暗号化信封は拒否します。required は独自 JSON エンドポイントにも適用でき、`excludePaths` より優先します。Crypto・管理鍵リング・registry が必要で、反対指定の重複は Handle 作成時に失敗します。crypto 対応 CLI テンプレートはサーバーとブラウザーを装配し、本番では db と共有 DB を使用します。非公開ポリシーと代理設定はブラウザーに渡さず、明文呼出側は公開の転送除外設定に従います。
|
|
413
|
+
|
|
414
|
+
サーバーの `_kit.yml` にある `trustedProxies` と `access` を `kit.createHandle` に明示的に渡します。例と生成テンプレートはアプリ初期化後に Handle を一度だけ生成します。`access` は既定 `false` です。`{ rules: [{ id: 'external', paths: ['/api/v1/external/**'], allowIps: ['192.0.2.0/24'] }] }` は一致する入口だけを、`global` はホストが扱う全要求を制限します。パスは配備プレフィックスを含みます。静的ホストが直接返す資源は Handle を通りません。
|
|
415
|
+
|
|
416
|
+
独立 YAML は `access: { file: './config/security/request-access.yml' }` と指定します。作業ディレクトリを基準に起動時だけ最大 1 MiB を読み、変更時は Handle を再生成します。全体条件と一致するすべてのルールを満たす必要があります。空のリストは対応する制限なし、時区は既定 UTC です。既存セッションも許可時間終了後は拒否します。preflight と鍵交換も判定し、通常の暗号化要求は HTTP 403 / `hai:common:104` の応答暗号化を保持します。
|
|
417
|
+
|
|
418
|
+
既定 `trustedProxies: auto` は通常のホストで転送ヘッダーを無視し、管理対象 Compose の非公開バックエンド構成だけでコンテナネットワークを採用します。独自の入口は実際の代理 IP/CIDR を指定し、`[]` で信頼を無効化します。解決後の `event.getClientAddress()`、ポリシー、IAM、監査は同じ接続元を使います。外側の代理・源 IP の喪失・環境変数と復旧は[リクエスト制御手引き](../../docs/usage/configuration/request-access.md)を参照してください。
|
|
419
|
+
|
|
420
|
+
## 開発とテスト
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
pnpm --filter @h-ai/kit test
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Kit に共通の init/close はありません。共有 cache・IAM・crypto はアプリが初期化と終了を管理します。型検査できる例は [JSDoc 規約(中国語)](../../.agents/instructions/module-conventions.md#jsdoc-与示例验证) を参照してください。認証は iam.auth.verifyToken で検証し、空でない token だけでは許可しません。
|
|
427
|
+
|
|
428
|
+
## ライセンス
|
|
429
|
+
|
|
430
|
+
Apache-2.0
|