diff options
Diffstat (limited to 'doc/Design.md')
| -rw-r--r-- | doc/Design.md | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/doc/Design.md b/doc/Design.md index 80b5acc..5a8d0f0 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -93,8 +93,9 @@ Agent Runtime ─── Session Store (memory) ├── Less Privileged AppContainer (LPAC) ├── NTFS ACL ├── Job Object - ├── WFP - └── Named Pipe + └── Named Pipe ── Task単位runas Process + ├── WFP dynamic session + └── loopback exemption │ ▼ Sandbox Process Tree @@ -121,6 +122,7 @@ src/ Agent.Core/ # Agent loop, session, prompt, ollama abstractions Agent.Planner/ # TaskGraph, TokenPERT, TokenCPM Agent.Windows/ # SandboxPolicy enforcement and Win32 interop + Agent.Admin/ # Sandbox Brokerの固定network処理を行う通常clientと昇格host tests/ Agent.Core.Tests/ Agent.Planner.Tests/ @@ -140,6 +142,7 @@ state/ ```text Agent.App -> Agent.Mcp -> Agent.Core -> Agent.Planner Agent.App -------------------------> Agent.Windows +Agent.App / Agent.Windows ---------> Agent.Admin.Client Agent.Core -> ISandbox/IToolExecutor interfaces Agent.Windows implements those interfaces ``` @@ -234,7 +237,7 @@ result: <final natural-language answer; completed only> ### 5.8 起動preflightとHTTP request log -- Appはtransport構築前に`WindowsPlatformCheck`を実行する。administrator/BFE/WFP probeが1つでも失敗した場合はhostをbuild/startせずfatalとする。 +- Appはtransport構築前に`WindowsPlatformCheck`を実行し、OS、Application Profile、BFE read-only open、固定Bundle pathの昇格用Executableとversionを検査する。Administrator判定、WFP transaction、filter add/delete probeは行わない。 - `StartAsync`完了後にだけ`IMcpHostObserver.Ready`を通知するため、「起動中」と「接続可能」を区別できる。 - HTTP middlewareはsocket endpointとrouting metadataだけをbounded in-memory queueへ`TryWrite`し、専用background writerがstderr、application log、`logs/http.jsonl`へ出力する。request threadはconsole/file I/Oを待たず、queue飽和時はrequestを止めずlog entryをdropして件数を警告する。 - forwarded header、credential header、query、payloadは保存しない。 @@ -539,7 +542,7 @@ network: - reparse chainを最終targetまで解決しcycle、missing target、非local filesystemを拒否する。 - `agent-mcp apply [config] [--yes]`だけがApplied Configuration Storeを更新する。全解決・validation完了前、または確認前にStoreを変更しない。 - `agent-mcp [config]`は現在のYAMLを再解決・再hashし、Applied Configuration snapshotとの完全一致とWindows preflightを検証してからMCP受付を開始する。個別の`serve`と`check`commandは持たない。 -- `agent-mcp show [config]`はdesired/resolved/appliedの権限matrixをtext表示し、StoreやACL/WFPを変更しない。管理者権限、BFE、WFP add/deleteを伴う完全なWindows preflightはServer起動時だけ実行する。 +- `agent-mcp show [config]`はdesired/resolved/appliedの権限matrixをtext表示し、StoreやACL/WFPを変更せずUACを発生させない。`apply`は通常ユーザー権限で固定Application Profileを作成または確認してApplied Configurationを保存する。Server起動時は固定SIDの導出、BFE open、helper整合性のread-only preflightだけを行う。 - languageとtransportはYAMLだけから取得し、CLI overrideを持たない。 ### 10.7 Effective Sandbox Policy @@ -607,9 +610,13 @@ WFP実装は複雑な汎用Firewallにしない。設計上の変換規則だけ 5. DNS解決失敗はdeny。 6. Task終了でTask固有WFP filterを削除する。 7. LoopbackもEffective Network Policyに明示されない限り許可しない。LANをlocalへ含めない。 -具体的なWFP layer、sublayer、filter condition、transaction APIは実装時にCodexがWindows SDKを確認して決定し、Security Integration Testで仕様を検証する。 +WFP mutationはTask内の初回`process_run`直前に`runas`で起動する昇格Processが行う。Task間でProcessを再利用せず、dynamic sessionをTask終了まで保持する。session終了時にBFEがdynamic objectを削除するため、filter IDをRecovery Journalへ保存しない。 + +`local`はLPAC network capability、`NetworkIsolationSetAppContainerConfig`によるAppContainer loopback送信許可、WFPによる`127.0.0.1`/`::1`の列挙port制限を組み合わせる。同APIは既存SID集合をGetして保持し、Application Profile SIDの追加前状態をRecovery Journalへ記録・復元する。Microsoft資料の`debugging purposes`という用途制約を既知制約とし、同API自体はSecurity Boundaryとしない。WFP permit単独、MSIX、独自proxyへfallbackしない。 ### 11.6 Named Pipe -Sandbox workerとのIPCが必要な場合はtask固有Named Pipeを使用し、DACLをApplication SIDとBrokerだけに限定する。TCP localhostを内部IPCに使わない。 +通常側がTask固有Named Pipe serverを明示DACL付きで作成し、昇格Processだけがclient接続する。DACLはlogon SIDとAdministratorsへ必要最小権限を与え、NETWORKをdenyし、default DACLを使用しない。通常側は`ShellExecuteEx/runas`相当で得たProcess IDと`GetNamedPipeClientProcessId`を照合し、昇格側もparent PIDと`GetNamedPipeServerProcessId`を照合する。 + +protocolはversion、Task ID、固定Application SID、Network Access Mode、解決済みIP/port、loopback復元状態を持つ長さ上限付きmessageに限定する。任意Executable、command、path、Security Descriptor、Registry、WFP layer/sublayer/action、Configuration/Policyを表現できるfieldを持たない。TCP localhost、独自RPC、汎用Network proxyへ拡張しない。 --- ## 12. ACL / Resource Transaction設計 ### 12.1 ACIDから借用する性質 @@ -623,7 +630,7 @@ Sandbox workerとのIPCが必要な場合はtask固有Named Pipeを使用し、D ```text CLEAN -> PREPARING journal create - -> APPLIED ACL/WFP/Job ready + -> APPLIED 初回process_run直前にACL/WFP/Job ready -> RUNNING sandbox process start -> CLEANING process stopped / task terminal -> CLEAN all task resources removed @@ -638,12 +645,13 @@ JournalはTaskごとのJSONとし、操作を実行する**前**にundoに必要 "task_id": "...", "state": "APPLIED", "acl_changes": [{"path":"...","change_id":"..."}], - "wfp_filter_ids": [123], + "network_loopback_before": false, "temp_paths": ["..."], "created_at": "..." } ``` 起動時にjournalが存在する場合、MCP transport開始前にRecovery Managerが逆順でcleanupし、全項目の不存在/復元を検査してからjournalを削除する。 +ACL/file/tempだけなら通常権限で復旧する。persistentなloopback entryがあれば起動時だけ昇格Processを起動して復旧し、UAC拒否時はMCP受付前にfatal終了する。WFP dynamic entryの旧Journalはupgrade互換のno-opとしてclean化する。 --- ## 13. File Transaction設計 ### 13.1 基本フロー @@ -699,7 +707,7 @@ Auditはappend-only JSONLを採用する。1 event = 1 line。 {"ts":"...","event":"file.write","task_id":"...","path":"...","before_sha256":"...","after_sha256":"..."} {"ts":"...","event":"policy.deny","task_id":"...","kind":"network","target":"example.com:443","reason":"not_allowlisted"} ``` -最低event集合: `task.start`, `task.end`, `tool.call`, `process.start`, `process.exit`, `file.write`, `file.delete`, `policy.deny`, `network.rule.apply`, `network.rule.remove`, `recovery.start`, `recovery.end`。各eventは可能な限りsession ID、task ID、内部error codeを持つ。 +最低event集合: `task.start`, `task.end`, `tool.call`, `process.start`, `process.exit`, `file.write`, `file.delete`, `policy.deny`, `network.rule.apply`, `network.rule.remove`, `recovery.start`, `recovery.end`, `sandbox.elevation.request`, `sandbox.elevation.success`, `sandbox.elevation.deny`, `sandbox.ipc.reject`。各eventは可能な限りsession ID、task ID、内部error codeを持つ。pipe名、IPC payload全文、UAC credentialは記録しない。 --- ## 16. Recovery・終了設計 ### 16.1 graceful stop @@ -790,6 +798,11 @@ closed Sessionは再利用しない。プロセス再起動後に以前のSessio | ST-25 | Entry (`PowerShell`/Python/.NET/`cmd`) × Descendant (`cmd`/PowerShell/Python/.NET/System32 utility) | 個別Approvalなしで起動し、Descendantが同じLPAC SID/Jobに残る。cmd内からはPATH解決名またはWorkspace相対pathを使用する | | ST-26 | protected DACL root/descendantと通常DACL rootのread/write/execute | protected bitを解除せず、許可ケースだけ成功。適用不能ケースは拒否し、cleanup後Security Descriptorが完全一致 | | ST-27 | baseline/Network capabilityの追加前後 | 必要な正方向ケースに加えFilesystem、Registry write、COM、Policy外Network、Job breakawayの負方向集合をすべて検証 | +| ST-28 | 通常tokenでのstdio/HTTP、show/apply、file-only Task | ServerとTaskが成功しUACが発生しない | +| ST-29 | Taskごとの初回/後続process_runとUAC拒否 | 初回だけ昇格し、同一Task内でhelperを再利用する。拒否は当該Taskだけfailed | +| ST-30 | helper crash、main crash、startup Recovery | WFP残留0、loopbackを元状態へ復元、Recovery UAC拒否はbind前fatal | +| ST-31 | Named Pipeのremote/別PID/別session/version/size/reconnect/double-apply | すべて拒否し、固定network操作以外をschemaで表現不能 | +| ST-32 | main/helper manifest、bundle path/version、native call graph | asInvoker/requireAdministrator、固定admin配置、WFP/loopback mutationがhelper側だけに存在 | --- ## 20. エラー分類 | Category | 例 | Task結果 | Retry | @@ -818,8 +831,9 @@ errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MC | 10 | Release/Operational E2E | self-contained Bundle単独起動、stdio/HTTP実利用 | ### 21.1 Release設計 -- `dotnet publish -c Release -r win-x64 --self-contained true` のfolder publishを配布元とする。 +- mainと昇格hostを同じversion/RIDで`dotnet publish -c Release -r win-x64 --self-contained true`し、mainをBundle root、昇格hostを固定`admin/` subdirectoryへ配置する。 - Bundleはversion付きZIP、展開済みdirectory、file manifest、SHA-256 checksumから成る。 +- main manifestは`asInvoker`、`admin/agent-mcp-admin.exe` manifestは`requireAdministrator`かつ`uiAccess=false`とする。mainは`AppContext.BaseDirectory/admin/agent-mcp-admin.exe`だけを参照し、PATH検索やConfigurationによる差替えを許さない。 - 起動時の相対pathは実行ファイルdirectoryを基準に解決し、ソースツリーを参照しない。 - 現行Operational E2Eは配布既定の`deepseek-v4-flash:cloud`を使用する。rate limit、接続失敗、model unavailableをapplication PASSとして扱わない。 --- |
