@h-ai/serv 0.1.0-alpha.68 → 0.1.0-alpha.70

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 CHANGED
@@ -226,7 +226,9 @@ Only authenticated protocol v2 is accepted. Independent random SM4-CBC and HMAC-
226
226
 
227
227
  Empty POSTs still send an authenticated envelope for empty plaintext. GET/HEAD carry it in X-Hai-Transport; bodyless responses such as 204/304/HEAD use the same header. Other requests/responses carry a JSON envelope and X-Encrypted: true. The client must match the original request context and HTTP status; missing markers, old formats and authentication failures reject. Exclude plaintext endpoints explicitly with excludePaths. CORS must allow and expose X-Hai-Transport. TLS authenticates handshake keys; this protocol does not replace IAM authorization.
228
228
 
229
- Fixed protocol budgets come from `@h-ai/crypto`'s `TRANSPORT_BODY_LIMITS`: 16 KiB key exchange JSON, 4 KiB per public key, 1 MiB plaintext, and 2 MiB + 32 KiB encrypted JSON, measured in UTF-8 bytes. The original stream is read incrementally; limits or cancellation stop/cancel it and release the reader without cloning and fully buffering it. Oversized requests return 413, oversized responses return 500, and invalid public keys return 400. Rebuilt requests preserve signals and clear Hono's body cache; rebuilt requests/responses remove stale Content-Length/Content-Encoding. These budgets are protocol constants, not YAML or environment settings.
229
+ Set `transport.maxPlaintextBytes` for both request and response JSON plaintext, measured in UTF-8 bytes: default 1 MiB, integer range 1 KiB–64 MiB. Clients learn the server limit during key exchange. `resolveTransportBodyLimits()` derives encrypted/proxy bytes as twice the plaintext limit plus 32 KiB. Handshake, public-key, header and replay constraints remain independent. Oversized streams are cancelled and readers released: requests return 413; oversized responses return an encrypted 500 capacity error, recognized as `TransportBodyTooLargeError` / `hai:crypto:070`. [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
230
+
231
+ serv/kit return an encrypted HTTP 400 time error for an out-of-window message only after request binding, signature and MAC verification. The client throws `TransportTimeError` (`hai:crypto:071`) with advice to synchronize device/server clocks and refresh. `api-client` maps it to `VALIDATION_FAILED` and preserves `cause.transportCode`. Requests are not replayed automatically and replay windows remain unchanged; tampered or unauthenticated messages retain the generic authentication failure.
230
232
 
231
233
  #### Token authentication (`iam` / `refreshCookie`)
232
234
 
@@ -702,6 +704,36 @@ pnpm --filter @h-ai/serv lint
702
704
 
703
705
  Declaration JSDoc examples import actual `@h-ai/serv` entries or published feature subpaths. Internal Hono, guard, router and transport examples are marked as internal module context and are not root APIs. Examples distinguish startup configuration exceptions, domain HaiResult, HTTP middleware statuses, and listener shutdown returning Promise<void>; default AI completion still returns non-streaming JSON only. Maintainers can run `node scripts/check-module-jsdoc.mjs --package serv` against actual declarations and internal lexical bindings. Compilation does not replace real HTTP, authorization or distributed replay-protection validation.
704
706
 
707
+ ## Optional feedback service and trusted scope
708
+
709
+ Mount `apiContract.feedback` with `createFeedbackProcedures` from `@h-ai/serv/features/feedback`. The runtime root does not load the feedback service. Initialize reldb/storage/reach/feedback in the host, then map verified sessions and deployment policy to feedback scope. A null resolver result or mismatched user is denied; the domain module also checks scope and permissions.
710
+
711
+ All HTTP responses, including health, 404, and middleware short circuits, carry a bounded X-Request-Id. For ordinary business logs, provide the optional scopeId from the existing createContext factory. It is never read from client headers. The HTTP boundary clears previous identities, and authenticated asynchronous operations inherit the current request and user.
712
+
713
+ ```ts
714
+ import { apiContract } from '@h-ai/api-contract'
715
+ import { feedback } from '@h-ai/feedback'
716
+ import { iam } from '@h-ai/iam'
717
+ import { serv } from '@h-ai/serv'
718
+ import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
719
+
720
+ const contract = apiContract.create({ feedback: apiContract.feedback })
721
+ const authenticate = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
722
+ const procedures = {
723
+ feedback: createFeedbackProcedures({
724
+ feedback,
725
+ resolveContext: context => context.scopeId
726
+ ? { scopeId: context.scopeId, userId: context.session.userId, permissions: context.session.permissions }
727
+ : null,
728
+ }),
729
+ }
730
+ const app = serv.createApp({
731
+ contract,
732
+ procedures,
733
+ createContext: async input => ({ ...await authenticate(input), scopeId: 'configured-product' }),
734
+ })
735
+ ```
736
+
705
737
  ## License
706
738
 
707
739
  Apache-2.0
package/README.ja.md CHANGED
@@ -226,7 +226,9 @@ const app = serv.createApp({
226
226
 
227
227
  空の POST も空平文の認証封装を送ります。GET/HEAD は X-Hai-Transport ヘッダー、204/304/HEAD など本文なし応答も同じヘッダーを使います。他の要求・応答は JSON 本文と X-Encrypted: true を使います。クライアントは元の要求コンテキストと HTTP status を照合し、マーカー欠落、旧形式、認証失敗では reject します。平文 endpoint は excludePaths で明示的に除外し、CORS では X-Hai-Transport を許可・公開してください。鍵交換の公開鍵認証は TLS に依存し、IAM の業務認可は別に必要です。
228
228
 
229
- 固定上限は `@h-ai/crypto` の `TRANSPORT_BODY_LIMITS` を共有し、鍵交換 JSON は 16 KiB、公開鍵は 1 個あたり 4 KiB、平文は 1 MiB、暗号文 JSON は 2 MiB + 32 KiB です。実際の UTF-8 バイトを元のストリームから逐次読み取り、超過・キャンセル時は停止・キャンセルして reader を解放します。clone して全量を読み取る処理は行いません。リクエスト超過は 413、応答超過は 500、不正な公開鍵は 400 です。再構築したリクエストは signal を維持して Hono の本文キャッシュを消去し、リクエスト・応答の古い Content-Length/Content-Encoding を削除します。上限はプロトコル定数であり、YAML・環境変数では設定しません。
229
+ `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)。
230
+
231
+ serv/kit は要求の結合、署名、MAC を検証した後、時間窓を超えた報文に暗号化した HTTP 400 時刻エラーを返します。クライアントは `TransportTimeError`(`hai:crypto:071`)で端末とサーバーの時刻同期・再読み込みを案内します。`api-client` は `VALIDATION_FAILED` に変換し `cause.transportCode` に原因を保持します。自動再送やリプレイ時間窓の緩和は行わず、改ざん・未認証の要求は従来の認証エラーです。
230
232
 
231
233
  #### Token 認証(`iam` / `refreshCookie`)
232
234
 
@@ -702,6 +704,36 @@ pnpm --filter @h-ai/serv lint
702
704
 
703
705
  宣言の JSDoc 例は実在する `@h-ai/serv` または公開 features サブパスから import します。内部 Hono・guard・router・transport は「模块内部上下文」と明記し、ルート API として扱いません。起動設定の例外、業務 HaiResult、HTTP middleware の status、listener の Promise<void> 終了を区別し、既定の AI completion は非ストリーミング JSON のままです。保守時は `node scripts/check-module-jsdoc.mjs --package serv` で実際の宣言と内部スコープに対して例をコンパイルします。実 HTTP、認可、共有リプレイ防止の検証を代替しません。
704
706
 
707
+ ## オプションのフィードバックサービスと信頼範囲
708
+
709
+ `@h-ai/serv/features/feedback` の `createFeedbackProcedures` で `apiContract.feedback` を接続します。通常のランタイム入口は feedback サービスを読み込みません。ホストで reldb/storage/reach/feedback を初期化し、検証済みセッションと配置ポリシーから範囲を作ります。null またはユーザー不一致は拒否し、ドメイン側も範囲と権限を検証します。
710
+
711
+ ヘルスチェック・404・中間件の早期応答を含む全 HTTP 応答に上限付き X-Request-Id を付けます。通常の業務ログでは既存 createContext の省略可能な scopeId を信頼できるホストから設定します。クライアントのヘッダーからは取得しません。HTTP 境界で古い認証情報を消去し、認証後の非同期処理に現在の ID とユーザーを伝播します。
712
+
713
+ ```ts
714
+ import { apiContract } from '@h-ai/api-contract'
715
+ import { feedback } from '@h-ai/feedback'
716
+ import { iam } from '@h-ai/iam'
717
+ import { serv } from '@h-ai/serv'
718
+ import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
719
+
720
+ const contract = apiContract.create({ feedback: apiContract.feedback })
721
+ const authenticate = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
722
+ const procedures = {
723
+ feedback: createFeedbackProcedures({
724
+ feedback,
725
+ resolveContext: context => context.scopeId
726
+ ? { scopeId: context.scopeId, userId: context.session.userId, permissions: context.session.permissions }
727
+ : null,
728
+ }),
729
+ }
730
+ const app = serv.createApp({
731
+ contract,
732
+ procedures,
733
+ createContext: async input => ({ ...await authenticate(input), scopeId: 'configured-product' }),
734
+ })
735
+ ```
736
+
705
737
  ## ライセンス
706
738
 
707
739
  Apache-2.0
package/README.md CHANGED
@@ -226,7 +226,9 @@ const app = serv.createApp({
226
226
 
227
227
  空 POST 也发送空明文的认证封装;GET/HEAD 将封装放在 X-Hai-Transport 头,204/304/HEAD 等无正文响应也使用同名头承载认证封装。其它请求/响应在 JSON 正文承载封装并带 X-Encrypted: true。客户端必须关联原请求上下文和 HTTP status,缺标记、旧格式或认证失败一律 reject;明文接口只能经 excludePaths 显式排除。CORS 须允许并暴露 X-Hai-Transport。握手公钥的真实性依赖 TLS,不替代 IAM 业务授权。
228
228
 
229
- 固定协议预算统一来自 `@h-ai/crypto` 的 `TRANSPORT_BODY_LIMITS`:握手 JSON 16 KiB,单个公钥 4 KiB,明文 1 MiB,密文 JSON 2 MiB + 32 KiB,均按 UTF-8 字节计数。原始流逐块有界读取,超限或取消会停止读取、取消流并释放 reader,不克隆后完整缓冲。请求正文超限返回 413,响应超限返回 500;无效公钥返回 400。重建请求时保留 signal、清空 Hono 正文缓存;重建请求/响应时清除旧 Content-Length/Content-Encoding。这些预算是协议常量,不是 YAML 或环境变量配置。
229
+ 业务容量通过 `transport.maxPlaintextBytes` 统一配置:默认 1 MiB,整数范围 1 KiB~64 MiB,请求和响应共用 UTF-8 明文字节预算。客户端从握手自动获取上限,密文与部署代理预算由 `resolveTransportBodyLimits()` 推导(两倍明文 + 32 KiB)。握手、公钥、认证头和防重放约束独立;超限取消原流并释放 reader,请求 413、响应为加密的 500 容量错误,客户端识别 `TransportBodyTooLargeError` / `hai:crypto:070`。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
230
+
231
+ serv/kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的报文返回加密的 HTTP 400 时间错误;客户端抛 `TransportTimeError`(`hai:crypto:071`),提示同步设备和服务器时间后刷新重试。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 保留该原因。不会自动重放请求或放宽防重放窗口;篡改或未认证报文仍返回通用认证失败。
230
232
 
231
233
  #### Token 认证(`iam` / `refreshCookie`)
232
234
 
@@ -702,6 +704,36 @@ pnpm --filter @h-ai/serv lint
702
704
 
703
705
  声明旁的 JSDoc 示例从实际 `@h-ai/serv` 或已公开的 features 子路径导入;内部 Hono/guard/router/transport 示例明确标注“模块内部上下文”,不代表根 API。示例区分工厂的启动配置异常、领域 HaiResult、HTTP middleware 状态码和监听句柄的 Promise<void> 关闭;默认 AI completion 仍只返回非流式 JSON。框架维护运行 `node scripts/check-module-jsdoc.mjs --package serv` 对实际声明和内部词法绑定编译示例,编译不代替真实 HTTP、授权与分布式防重放验收。
704
706
 
707
+ ## 可选的反馈服务与可信范围
708
+
709
+ 通过 `@h-ai/serv/features/feedback` 的 `createFeedbackProcedures` 挂载 `apiContract.feedback`;只导入运行时根入口不会加载反馈服务。宿主先初始化 reldb/storage/reach/feedback,再从已验证的会话与部署策略映射反馈范围。resolveContext 返回 null 或用户不匹配时拒绝访问;领域模块再次校验范围与权限。
710
+
711
+ 所有 HTTP 响应(包括探活、404、中间件短路)携带有界 X-Request-Id。普通业务日志需由已有 createContext 的可选 scopeId 建立可信范围,scopeId 从不读取客户端头。HTTP 入口清理旧身份,认证后的异步业务日志自动继承当前请求 ID 与用户。
712
+
713
+ ```ts
714
+ import { apiContract } from '@h-ai/api-contract'
715
+ import { feedback } from '@h-ai/feedback'
716
+ import { iam } from '@h-ai/iam'
717
+ import { serv } from '@h-ai/serv'
718
+ import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
719
+
720
+ const contract = apiContract.create({ feedback: apiContract.feedback })
721
+ const authenticate = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
722
+ const procedures = {
723
+ feedback: createFeedbackProcedures({
724
+ feedback,
725
+ resolveContext: context => context.scopeId
726
+ ? { scopeId: context.scopeId, userId: context.session.userId, permissions: context.session.permissions }
727
+ : null,
728
+ }),
729
+ }
730
+ const app = serv.createApp({
731
+ contract,
732
+ procedures,
733
+ createContext: async input => ({ ...await authenticate(input), scopeId: 'configured-product' }),
734
+ })
735
+ ```
736
+
705
737
  ## 许可证
706
738
 
707
739
  Apache-2.0
@@ -2,8 +2,8 @@ import * as _orpc_server from '@orpc/server';
2
2
  import * as zod_v4_core from 'zod/v4/core';
3
3
  import * as zod from 'zod';
4
4
  import { AIFunctions } from '@h-ai/ai';
5
- import { S as ServContext } from '../serv-context-C48WdBcd.js';
6
- import { A as AuthenticatedServContext } from '../serv-router-BX8hgYxo.js';
5
+ import { S as ServContext } from '../serv-context-B-AxU8hj.js';
6
+ import { A as AuthenticatedServContext } from '../serv-router-C8dSz2k-.js';
7
7
  import '@h-ai/core';
8
8
 
9
9
  /**