阅读入口:优先让 AI Agent 使用 https://www.miniyun.com/docs/ai-agent.md 和 https://www.miniyun.com/docs/skills/oa-app-integration/SKILL.md 完成集成与上传发布;原理说明 /docs/principles;高级与扩展原理说明 /docs/advanced。 架构先读:核心底座、公共能力扩展、业务应用是三个职责层次。核心负责身份/授权/路由/隔离;扩展提供共享服务;应用负责H5与业务规则和数据。模块是交付单元,不是第四层。工作流等为接入方向,不代表已经安装。详见 /docs/#platform-layers 和 /docs/public-extensions.md。 PUBLIC EXTENSIONS v0.19: /docs/public-extensions.md. Providers require explicit operator/admin approval. Consumers declare versioned dependencies and capability grants. Trusted caller metadata is set by the kernel. Scheduler is an optional bundled extension (OA_SCHEDULER_ENABLED=false disables it without deleting data). Legacy capabilities remain compatibility adapters. Scheduler v0.18.0: /docs/scheduled-jobs.md. Cron invokes registered module actions under the saved account and bounded permissions; no arbitrary shell, URL or reflection. No automatic business retries. Long running business work must be asynchronous inside the application. TEST SYSTEM v0.17.9: www.miniyun.com explicitly uses OA_REGISTRATION_ROLE=system-admin. Verified email registrations and enabled existing self-registered test users become system administrators. Keys following account permissions inherit administrator permissions. Other deployments default to registered-user; module business permissions still require role assignment. This test policy does not make application interfaces public or bypass authentication. v0.17.5 自助发布:所有启用账号均可用个人API Key提交、测试、审核和发布自己名下的应用,无须管理员审批。注册邮件包含自动生成的90天Key及curl示例;提交和发布后通知本人。只允许管理自己的模块ID,模拟必须通过,个人发布要求Docker,敏感公共能力不自动开放。旧文中管理员审批条款现由应用所有者确认替代。详见 /docs/self-service-publishing.md。 PUBLIC MODULE CONTRACT v2 (platform v0.17): language-neutral JSON-RPC 2.0 over stdio; Python SDK + Go ServeJSON in devkit.zip. Read /docs/module-jsonrpc.md first. Existing protocol v1 below is an optional Go compatibility adapter, not a requirement for new languages. Interpreter images/commands are operator-owned profiles; packages cannot choose Docker privileges. COLLAB PLATFORM MODULE DEVELOPMENT GUIDE Personal API Keys (v0.6): signed-in users self-serve POST /api/account/api-keys {name,expires_days:30}; GET the same URL lists only their key metadata; POST /api/account/api-keys//revoke {} revokes. Browser session+CSRF required for key lifecycle. The api_key secret appears once; only SHA256 hashes are stored. Use Authorization: Bearer for existing business/admin APIs without cookies or CSRF. Default scopes follow the owner's live permissions; optional permissions:[] narrows to explicit grants (empty array grants none). Account disabling, expiry and revocation take effect immediately. Keys cannot manage keys or login authentication. All registered users can submit applications and query their own submission IDs; simulation/approval and module lifecycle still require modules.manage. One personal key works across modules, with no per-module submission token requirement. Use scripts/agent_publish_module.py with OA_API_KEY in environment and optional --publish. Existing scoped submission tokens remain compatible. Protocol: 1. Platform: Windows amd64 / Linux amd64. UI: mandatory self-contained H5 (platform v0.9). Runtime: native executable via HashiCorp go-plugin net/rpc; Linux Docker isolation selected by operator OA_MODULE_RUNTIME=docker. Native development mode is trusted-code only. This guide, /docs/api.json and /devkit.zip?v=0.20.4 are embedded in platform.exe. The EXE does not need a source checkout to serve them. 1. Download SDK GET /devkit.zip?v=0.20.4. Extract into your NEW module source directory. Archive has platform-sdk/go.mod and platform-sdk/modulekit/module.go. The SDK is complete; do not import platform internals. Create go.mod: module example.local/workorder go 1.24.0 require collab.local/platform v0.0.0 replace collab.local/platform => ./platform-sdk Run go mod tidy before building. First build requires Go and dependency download access. EXEs require neither Go nor internet at runtime. 2. Implement Go interface import "collab.local/platform/modulekit" Business has: Describe() modulekit.Descriptor Execute(modulekit.Request, modulekit.Host) (modulekit.Response, error) main must call modulekit.Serve(&YourBusiness{}). Descriptor fields: ID, Name, Version, Protocol (1), Description, optional Actions. Action has Name and Label only. ID/Version/Protocol must match manifest. There is no Field, Fields, View, CreateLabel, record_columns, statuses or owner_only UI schema. H5 owns all controls, layouts and table columns; Execute owns business validation. Request fields: Action string (create|list), Payload json.RawMessage, RequestID string, ActorID string. Response fields: Data json.RawMessage, Error string, Code string. For validation errors return Response{Code:"INVALID_ARGUMENT",Error:"Chinese user-readable message"},nil. Transport/internal failures may return a Go error; host then stops module, requiring a start action. Unknown actions return Code NOT_FOUND. 3. Call host capabilities (reverse RPC) Host has Call(modulekit.HostRequest) (modulekit.HostResponse,error). HostRequest fields: Service string, Collection string, Payload json.RawMessage, RequestID string, ActorID string. HostResponse fields: Data json.RawMessage, Error string, Code string. Always propagate RequestID and ActorID from the current Execute input. Host verifies them against the active request and its connection-bound module ID. No calls after Execute returns, no asynchronous host calls or cached actors. Service identity.get: returns JSON object {id,name,mode}. Call it in create to demonstrate using the platform identity service. Service records.list: set Collection, returns JSON array of Record, newest first (empty array if none). Service records.create: set Collection and Payload (JSON object, max 32 KiB). Host generates metadata and atomically persists record + audit + idempotency result. Collection matches ^[a-z][a-z0-9_-]{0,39}$. Host automatically partitions by module ID; no arbitrary module or filesystem path arguments. Record fields: ID string json:id, CreatedAt string json:created_at, CreatedBy string json:created_by, Values json.RawMessage json:values. For list and create return Response{Data: reply.Data, Error: reply.Error, Code: reply.Code},nil after handling transport errors. Example: reply,err := host.Call(modulekit.HostRequest{Service:"records.create",Collection:"tickets",Payload:normalized,RequestID:r.RequestID,ActorID:r.ActorID}) if err != nil { return modulekit.Response{},err } return modulekit.Response{Data:reply.Data,Error:reply.Error,Code:reply.Code},nil 4. Workorder requirements for example Fields: title required text max 120 Unicode codepoints; reporter required text max 60; priority required select with exact options 普通,紧急; description optional textarea max 2000. Apply strings.TrimSpace on business fields and validate server-side. Reject unknown JSON fields and invalid types using json.Decoder.DisallowUnknownFields. Do not accept caller-provided status or metadata. Default priority to 普通 only when omitted/empty. Set status="已登记" in backend. Preserve normalized field order using a struct when marshaling, so create retries have stable payload. Action list uses records.list with tickets collection. Action create validates input, calls identity.get, then records.create. Each module ships its own H5 UI and calls window.PlatformUI. Do not edit the platform shell or store authoritative business data in the frontend. Request timeout: 10s per RPC. A timeout may happen after commit. Reuse request_id and exact business content when retrying. A reused key with different normalized payload returns CONFLICT. Host serializes actions in this prototype. 5. Package go mod tidy go build -trimpath -o workorder.exe . Create manifest.json: {"id":"workorder","name":"工单登记","version":"0.1.0","protocol":1,"entry":"workorder.exe","capabilities":["identity.get","records.list","records.create"],"ui":{"mode":"h5","entry":"ui/index.html","bridge_version":1,"files":["ui/index.html","ui/app.js"]}} Place manifest, executable and declared ui/ assets inside /modules/workorder/. Platform checks entry path, matching descriptor, protocol, grants. Discovery alone never executes a new module. 6. Register through platform HTTP GET / first and keep collab_local HttpOnly SameSite Lax cookie. GET /api/bootstrap returns csrf_token and requires_setup. Before protected API calls: if requires_setup is true, POST /api/auth/setup {name,password} to initialize local-admin; otherwise POST /api/auth/login {username,password}. Passwords have at least 12 characters. Replace your CSRF token with the token returned by setup/login, and keep the rotated session cookie. POST /api/auth/logout ends the session. POST requests use Content-Type: application/json and X-CSRF-Token plus cookie. POST /api/modules/register body {"id":"workorder","capabilities":["identity.get","records.list","records.create"],"ui":{"mode":"h5","entry":"ui/index.html","bridge_version":1,"files":["ui/index.html","ui/app.js"]}} The local administrator approves all listed capabilities by this request. Only install trusted binaries. GET /api/modules returns discovered and registered modules; running ones include descriptor. POST /api/actions/workorder/create body {"request_id":"a-unique-value-at-least-16-chars","payload":{"title":"维修打印机","reporter":"张三","priority":"普通","description":"无法打印"}} POST /api/actions/workorder/list body {"request_id":"a-different-unique-value","payload":{}} GET /api/audit returns latest 100 audit entries. POST /api/modules/workorder/stop body {}: stop now; registered module loads on next platform restart. POST /api/modules/workorder/start body {}: start/restart registered module. POST /api/modules/workorder/unregister body {}: stop and forget registration, retain business data. Unknown browser-supplied actor_id is rejected; host uses the authenticated account ID. Menu visibility and HTTP/RPC permissions follow assigned roles; do not assume local-admin for other accounts. 7. Verify Read these pages from the RUNNING platform before writing module source. Download and use SDK from platform, rather than referencing internal implementation. Record response hashes and build/test results. Verify register; packaged H5 UI; valid create/list; required-field and enum errors; duplicate request returns same record; changed payload same key conflicts; audit; stop/start; platform restart restores registration and data. Module EXE is a child process, not a standalone public web server. Running it directly prints a plugin warning; start through platform. 8. Limits Local-only trusted module demo. Local account login and role authorization are implemented; no tenants, enterprise SSO, sandbox, dynamic SQL, enterprise workflow, unrestricted frontend network access, database, or automatic migration. Department membership does not isolate business records; business.read covers all modules. Persistence is JSON whole-file snapshots with synchronized replace, with a single-process data-directory lock. Stop platform before copying data for backup. Do not run multiple hosts on the same data directory. Registry persists approval; stop is temporary, unregister disables loading on restart. The host supervises unexpected module exits with bounded automatic restart; intentional stop suppresses recovery until explicit start or host restart. See section 11. 9. Administrative console (platform v0.2) GET /api/admin returns role-filtered departments/users/roles and revision. Save via POST /api/admin/departments, /users or /roles with revision from the current view; stale writes return 409. Permissions: modules.read, modules.manage, business.read, business.create, org.manage, users.manage, roles.manage, audit.read. Roles are unioned; business.read grants all-module record access. User managers cannot grant or manage permissions above their own. Built-in system-admin and the initial local-admin account are protected. Account disabling, password resets and permission changes take effect on existing sessions. Authenticated sessions survive restarts, expire after 7 idle days, renew at most once per 5 minutes of activity, and require reauthentication after 30 days. Password hashes use PBKDF2-HMAC-SHA256 (600000 iterations) with a per-password random salt; API responses omit credentials. Module registration still requires manifest capability approval in addition to user permissions. Protocol v1 and the module SDK are unchanged. 10. Module-scoped permissions and role templates (platform v0.3, RPC remains protocol 1) Manifest may declare permissions with id/name/action, for example: "permissions":[{"id":"reimbursement.read","name":"查看报销","action":"list"},{"id":"reimbursement.create","name":"提交报销","action":"create"}] Declare these two base actions; optional workflow actions are described in section 15. IDs must be .read and .create. Omitting permissions synthesizes these defaults for older modules. Optional role_templates contain id/name/description/permissions. Each role ID starts with - and obeys the normal 40-character identifier limit. Template permissions may ONLY reference that module's declared permissions, never platform or another module's permissions. On successful module registration the platform persists its permission catalog and creates missing role templates. Templates never assign users, and re-registering never overwrites an existing role edited by the administrator. Use the unified Roles and Permissions page to select permissions grouped by module. Assign one or more roles in User Management. A module-only role does not need modules.read: module discovery automatically returns only that account's permitted modules. Both HTTP actions and module reverse calls enforce the connection-bound module's read/create permission. Existing business.read/business.create permissions remain explicit all-module compatibility grants, including future modules. Do not combine these broad roles with a scoped role if the intention is access to just one module. Unregistering retains role assignments and marks catalog entries inactive; reinstall restores availability without resetting administrator edits. Stopping a module retains active registration and catalog entries. Permission namespaces modules/business/audit/org/users/roles are reserved module IDs. A manifest has at most 20 role templates. 11. Windows service host (platform v0.3.1, module protocol remains 1) Administrator terminal: platform.exe service install, then platform.exe service start. Commands: install|start|stop|restart|status|uninstall. Optional -service-name (default CollabPlatform). The internal "service run" command is for SCM only. Install records absolute -addr/-data-dir/-module-dir/-log-dir paths, delayed automatic start and LocalService account. Per-service SID grants modify on data/logs, read/execute on modules and platform binary. No module is separately installed as a Windows service. The host owns child module processes via Windows Job Objects. On normal stop it drains HTTP requests and closes modules; on host crash the OS cleans its module process trees. Unexpected module exits and RPC failures trigger at most 3 automatic starts with approximately 1/2/4-second backoff. Manual module stop suppresses restarts. Manual start resets the budget; 10 minutes of continuous healthy running clears the budget. SCM recovery restarts the host after 5/15/60 seconds for successive failures, then stops trying; its failure-count reset period is 1 hour. Normal service stop does not trigger recovery. GET /api/modules additionally exposes process_id for running modules and restart_count. These are operational metadata, not authorization inputs. Logs: logs/platform.log (5 MiB threshold, 3 backups), configurable with -log-dir. status prints service account, PID, binary path and startup settings. Stop service before replacing binaries or backing up data. Uninstall removes service registration but preserves data/modules/logs and per-service SID ACL entries. Moving the binary requires reinstalling service with the original data paths. Authenticated browser sessions are durably saved in data/sessions.json (token SHA256 keys only, mode 0600); logout is durable. Embedded Chinese operations guide: /docs/service. 11. Self-submission and administrator-controlled publication (platform v0.4) A candidate submission NEVER launches its EXE or installs roles into production. An administrator with modules.manage creates a scoped token: POST /api/publishing/tokens {module_id,expires_hours:24}. The clear token is returned once, stored only as a hash, may be revoked and expires within at most 168 hours. The module developer or deployment script POSTs a ZIP body to /api/module-submissions with Content-Type: application/zip, Authorization: Bearer , and X-Submission-Request-ID (16-128 chars). No browser cookie/CSRF is required for this bearer endpoint. Tokens can only submit/query their module; they cannot test, approve or access business data. The ZIP root contains manifest.json, its declared native executable (Windows EXE or Linux ELF for the server architecture), and optionally simulation.json. H5 mode additionally includes exactly the ui.files assets under ui/ (see /docs/h5-ui.md). Limit: 64MiB compressed AND decompressed; manifest/simulation config max64KiB each. Undeclared paths, directory entries, links, duplicate and undeclared files are rejected. Current publication flow requires identity.get, records.list and records.create capabilities. GET /api/module-submissions/ with the same scoped bearer token returns status. Reusing the same request ID with the same ZIP returns the original submission; different bytes conflict. Administrator browser endpoints (session cookie + X-CSRF-Token, modules.manage required): GET /api/publishing POST /api/publishing/tokens//revoke {} POST /api/publishing/submissions//test {} POST /api/publishing/submissions//approve {capabilities:[...exact requested caps...],note:"..."} POST /api/publishing/submissions//reject {note:"required reason"} Simulation and approval start asynchronous jobs (202); poll the list/status. States: pending -> testing -> ready/failed; ready -> publishing -> published/failed; pending/ready/failed -> rejected. Failed candidates require re-testing. Published/rejected candidates cannot be resubmitted as the same workflow item; submit a new ZIP/request ID instead. simulation.json example: {"valid_payload":{...valid business data...},"conflict_payload":{...different but valid business data...}}. Fixture data is only used in an isolated temporary database. Tests cover package hash, Describe consistency, empty list, required/unknown fields, valid create, idempotence/conflict, reader denial, atomic audit and database/registration reload. This EXECUTES the candidate with the platform service OS identity; data isolation is NOT an OS sandbox or malware check. Review source before starting. Approval is bound to the tested ZIP hash, is audited, requires explicit capability approval, and attempts to restore the previous EXE/registry if startup fails. Prior packages remain under modules/.releases//previous. No migration or production state.json replacement is performed. Interruptions are reconciled at next startup or marked failed for a new simulation. Windows service installs now grant only its dedicated service SID Modify access to modules/ as well as data/logs, so an approved release can be installed. Existing service installations need that module-directory SID grant updated; EXE itself remains read/execute. CLI helpers: scripts/package_module.py and scripts/submit_module.py. Provide MODULE_SUBMISSION_TOKEN via environment, not a public URL or repository file. See docs/module-publishing.md for an end-to-end simulation plan. 12. Linux HTTPS deployment Build with scripts/build-linux.ps1 (GOOS=linux GOARCH=amd64 CGO_ENABLED=0). Linux manifest entry is an extensionless filename such as leave, and the executable must be ELF amd64. Run the platform with -public-origin https://www.miniyun.com behind the loopback HTTPS proxy. Cookies are Secure; the exact HTTPS origin and loopback forwarded HTTPS requests are required. Use systemd for service lifecycle, including SIGTERM draining and child process cleanup. Windows packages must be rebuilt and resubmitted before Linux publication. 13. MANDATORY business module UI style: adminui-v1 Every business module MUST follow /docs/ui-style.md and /docs/ui-style. The SDK includes UI_STYLE.md and ui/index.html. Use the shared purple brand, light sidebar, 16px cards and theme tokens, supporting light/dark modes and narrow screens. All modules ship their own HTML forms, tables and layouts under declared ui/ assets. Descriptor contains only backend identity and actions; permissions and role_templates remain in manifest. See /docs/h5-ui.md. Do not duplicate the platform login or sidebar. Visual checks are required in addition to protocol simulations. 14. Email code login When Resend is configured, email code is the default and password login is disabled. Users must be enabled and have a unique verified email; Email self-registration is enabled in v0.6.1: a verified new email creates a minimal registered user; existing emails sign into their bound user. Disabled accounts cannot re-register. POST /api/auth/email/request {email} returns challenge_id, expires_in=300 and resend_after=60. POST /api/auth/email/verify {email,code,challenge_id} rotates the session and CSRF on success. Both endpoints require the browser session cookie, CSRF and same Origin. Codes are single-use, bound to the requesting browser, and allow at most 5 attempts. Only hashed codes live in memory; restart invalidates outstanding codes. Environment: RESEND_API_KEY, OA_MAIL_FROM, OA_ADMIN_EMAIL (initial administrator binding only). Do not put secrets in SDKs or manifests. 15. Backend workflow actions (platform v0.9, RPC protocol 1) Descriptor optionally declares Actions [{Name,Label}]. Each action edit|submit|publish|reject MUST have a manifest permission .. Up to four additional actions. H5 implements the editor, buttons, state visibility and table; no automatic workflow UI exists. HTTP POST /api/actions// uses the existing request_id/payload envelope. Extended actions require their explicit scoped permission: legacy business.create NEVER grants edit/submit/publish/reject. The built-in system-admin resolves module permissions dynamically; no ordinary user receives new rights automatically. Both HTTP and delegated RPC re-check current permissions. identity.get now also returns permissions ([]string). Modules enforce ownership and business state transitions. H5 button visibility is not authorization. records.create may append a revision during any permitted write action, and audit records identify the operation (records.edit/submit/publish/reject). records.list remains read-only. Collection and actor isolation are unchanged. Immutable revisions can be folded by the module into the current article list without adding mutable record storage. Include action, actor and normalized input in idempotence checks, and reject stale revisions before writing. The bundled CMS example uses editor (read/create/edit/submit) and chief (read/publish/reject) templates. Draft -> pending -> published or returned -> pending. Editors edit only their own draft/returned articles. The CMS publishes into the authenticated application's published list; this does not create a public anonymous website. Published articles are locked in this initial version. 17. Email account binding (v0.6.1) POST /api/auth/email/request {email} sends a session-bound code for login or registration. POST /api/auth/email/verify {email,code,challenge_id} consumes the code. If no user was bound when the code was issued, verification atomically creates an enabled user with immutable generated ID and the registered-user role (modules.submit only). Existing verified email login preserves the same user ID, roles, content and API Keys. No account is created by requesting a code, by a wrong code, or for a disabled account. An email binding changed after code issuance requires a new code. The response includes registered:true only for first registration. Browser redirects new registrants to their API Key page. Email is a unique changeable login address, never the ownership key; content and personal keys belong to User.ID. Registered users have no employee/CMS/admin access until administrators assign roles. All existing OTP lifetime, browser binding, replay protection, retry and send limits remain. 18. Read-only organization directory (v0.7, additive protocol 1) Manifest read_only:true declares exactly one permission .read (action=list) and capability directory.read only. The module ships its own H5 organization tree and contact search; no host-specific directory renderer or query Field is used. list calls Host directory.read with the current ActorID/RequestID and returns its Data. All write actions are denied by the host even for administrators. No organization data is duplicated in module records. directory.read returns {departments:[{id,name,parent_id}],contacts:[{id,name,email,department_id}]}; credentials, roles, disabled accounts and unassigned registration-only users are excluded. Missing phone/job fields are not invented. Capability approval and current module read permissions (including delegated API Key restrictions) are both required. The directory-reader template grants directory.read only and assigns nobody automatically. Read-only packages use the same submit -> isolated simulation -> approve workflow. Their dedicated simulation verifies organization fixtures, disabled-account exclusion, blocked writes, restricted delegation, no record writes and restart recovery instead of create/idempotence probes. simulation.json may contain empty valid_payload/conflict_payload objects. Use fresh SDK from this version. The directory app source is examples/directory/. Sessions use Secure (public HTTPS), HttpOnly, SameSite=Lax so external top-level navigation preserves login; mutating browser APIs still require matching Origin and CSRF. GET /api/bootstrap initializes a missing/expired anonymous session so users can sign in without manually reloading. Authentication and role changes are checked against live account state on every request. 19. Mandatory H5 UI (platform v0.9, RPC protocol 1) Manifest ui is required: {mode:"h5",entry:"ui/index.html",bridge_version:1,files:["ui/index.html","ui/app.js"]}. Missing ui and generated mode are rejected on package submission and registration. See /docs/h5-ui.md for the complete contract, security boundaries, migration, SDK calls and package limits. The host only mounts a sandboxed iframe and an authorized MessageChannel bridge; it never generates business forms/lists. Assets share the ZIP fingerprint, candidate, installation and rollback with the native executable. SDK includes H5_UI.md and h5/ examples. simulation.json may provide invalid_payload (defaults to {}), valid_payload and conflict_payload; no form metadata is consulted. DOCKER RUNTIME v0.10 Each app and publication simulation runs as a non-root, read-only, network-disabled container with bounded memory/CPU/PIDs. No host business data, credentials or Docker socket are mounted. All data flows through existing capability/actor-checked host RPC. Container launch flags/images/mounts cannot be declared by uploaded manifests. Docker failures do not fall back to native. /api/docker and /api/docker/{module}/logs require modules.manage; POST /api/docker/{module}/start|stop|restart {} controls only OA apps. Manual stop persists across host restarts. cloud.request is reserved for cloud-admin and disabled in simulation. Docker shares the host kernel; stronger isolation may be needed for hostile public code. 20. Docker 模式权限控制方案(当前实现) Docker 模式的权限控制方案 采用“容器限制系统资源、底座授予业务能力、应用校验业务规则”的三层授权。容器只获得运行本模块所需的最小环境;能够启动程序不代表能够读取业务数据,也不代表拥有当前用户的所有权限。 控制范围 | 当前底座实际策略 | 文件与持久化 | 根文件系统只读,只挂载本模块的只读程序副本和独立 Unix socket 通信目录。/tmp 是 16 MiB 的内存临时盘,带 noexec、nosuid、nodev,容器删除后不保留。宿主业务目录、其他模块目录、云凭据和 Docker socket 不挂载到容器。通信目录是可写宿主挂载,当前尚无磁盘容量配额;16 MiB 只约束 /tmp,不代表应用全部写入的上限。业务持久化必须通过底座服务,不能直接访问宿主数据库文件。 | 网络与端口 | 使用 --network none,不发布端口,不共享宿主网络。模块不能直接访问公网、其他容器服务或云实例元数据;容器自身回环仍存在。H5 通过浏览器消息桥,后端通过专用 Unix socket 调用底座,不需要为模块开放 HTTP 端口。 | 用户与系统调用 | 以非 root 用户运行,删除全部额外 Linux capabilities,启用 no-new-privileges 和 Docker 默认 seccomp;不开放特权模式、宿主 PID 命名空间或设备透传。各容器隔离进程与文件视图;当前未为每个应用单独分配宿主 UID,不能把 UID 相同理解为可共享宿主目录。 | 资源与可用性 | 每模块内存上限 192 MiB、禁用 swap、CPU 配额 0.5 核、最多 64 个进程/线程、1024 个文件描述符。当前小规格服务器上,所有应用容器合计内存上限 256 MiB、CPU 配额 1.5 核、512 个任务;这是共享总预算,不是每个应用的保底份额。超过限制可能被限速或终止,不能保证恶意模块不会造成资源竞争。 | 数据、动作与 API Key | 容器隔离之外,底座继续核验已批准 capability、当前账号权限和 API Key 的限权范围,绑定模块 ID、ActorID 和请求号。records.* 按模块隔离;目录资料通过 directory.read 等专用服务授权。租户、部门、记录归属和业务状态不能由 Docker 判断;records.* 已按角色的模块/动作数据范围过滤;外部连接器和通讯录另行授权,应用继续校验业务状态。 | 外部服务与密钥 | 模块不能自行获取生产密钥或随意出网。开发环境管理使用受控 cloud.request 连接器:底座限定模块、操作者、动作及固定原站接口,并在宿主侧签名、审计和去重。当前没有面向任意应用的通用 HTTP 代理;新增外部服务需由底座实现并审核专用连接器。 | 发布、运维与日志 | 线上候选包模拟同样使用容器和独立数据,禁用真实云连接器。启动失败不回退本机执行。modules.manage 用户可查看容器状态和最近日志、启动、停止及重启;接口只接受 OA 模块 ID,不接受任意 Docker 命令、exec、镜像、挂载或清理参数。日志按每容器 1 MiB × 2 轮转,管理界面显示最近日志;手动停止状态跨底座重启保持。 | 开发者如何申请权限 在 manifest.capabilities 声明实际需要的底座服务,在 permissions 和 role_templates 声明模块业务动作;管理员审核后授予 capability,再给用户分配业务角色。普通注册用户仅可提交候选包,不自动获得业务权限。已有角色中的 business.read/business.create 是可覆盖以后模块的兼容权限,敏感应用应像 cloud-admin 一样要求精确的模块权限。API Key 默认跟随所属账号的实时权限,可能随账号授权扩大;建议显式限制 permissions,密钥有效权限始终不能超过所属账号。以 CMS 为例:编辑申请撰稿与提交,总编申请审核发布;Docker 无法区分这两个角色,必须由底座和 CMS 后端共同检查。 当前容器参数采用平台统一模板,manifest 不支持开发者自行指定 Docker 参数、目录、网络、设备、镜像或逐应用资源额度。新增文件访问、外部接口或更大额度,应先说明用途、数据范围、目标服务及资源需求,由平台运维评估和调整,不应通过特权容器或挂载 Docker socket 实现。 隔离边界与后续增强 Docker 能限制程序能接触的系统资源,不能证明上传包没有木马,也不能判断文章能否审核、发票能否报销或已经授予的业务操作是否合理。容器共享宿主 Linux 内核,Docker 控制端属于高权限受信任组件;当前使用系统 Docker,尚未启用 Rootless、gVisor、独立执行主机或虚拟机。对公开接收的高风险代码,应继续增加更强运行隔离,并维护内核和运行时更新。当前也未统一限制通信目录磁盘用量、业务记录总容量、磁盘 I/O 或底座服务调用频率,不能把容器 CPU/内存配额理解为完整的存储与 API 配额。现有措施不等于全面的恶意程序检测,也不保证业务数据的租户隔离。 参考:Docker 安全模型、Docker 资源限制。 参考:https://docs.docker.com/engine/security/ 和 https://docs.docker.com/engine/containers/resource_constraints/ Data scope contract v0.11: MUST read /docs/data-scopes.md (SDK DATA_SCOPES.md). The host filters records.list by current roles, module, action and API Key restrictions. New records receive trusted owner_id/department_id. Restricted workflow writes MUST send HostRequest.RecordID pointing to a same-module, same-collection original; inherited ownership is immutable. New roles default to self; old roles retain explicit all scope until administrators narrow them. 模块管理详情:后台模块管理按模块展示运行参数、只读清单及前端资源、能力批准、动作权限、实际关联角色与数据范围、最近发布记录。GET /api/modules/{id}/details 需要 modules.manage;实际角色另需 roles.manage 或 users.manage,启用用户数另需 users.manage。敏感服务器集成配置只返回是否已设置,不返回凭据或环境变量。声明配置随新版本审批发布,业务配置由模块内部管理。 21. 单机并发与请求级身份绑定(v0.13) 单机并发、请求身份与过载保护(v0.13) 不同模块的业务调用可以并行,执行期间不再持有底座模块管理器的全局锁。同一模块仍串行执行,避免旧协议回调绑定和现有多步骤审批产生竞态;这不是同一模块的多副本扩容。JSON 存储仍采用串行写入,本次未迁移数据库。 每次调用通过现有协议的 Initialize 建立独立的反向 RPC 连接,绑定不可变的模块、操作者、动作、请求号与 API Key 限权快照。执行结束或超时会关闭该连接;旧回调不能借用下一次请求的身份,即使重用了请求号。底座在回调时仍按账号实时状态核验角色和数据范围。现有协议 v1 二进制无需重编译。 保护项 | 默认策略 | 请求期限 | 每次动作调用总预算 10 秒,涵盖管理器访问、排队、回调初始化和模块执行。客户端断开会取消请求;已开始的写入不能保证撤销。 | 总量与用户并发 | 底座最多接纳 16 个动作请求,每用户最多 4 个,均包含排队中的请求。同一账号的浏览器与多个 API Key 共用用户额度;16 是接纳槽位数,不代表 16 个模块执行线程。 | 单模块排队 | 每模块最多接纳 4 个请求,即 1 个执行、最多 3 个等待;排队最长 250 ms。管理器正在处理启停、安装等操作时,也只允许短暂等待,随后返回繁忙,不无限堆积。 | 用户速率 | 按用户 ID 的令牌桶,平均每秒补充 20 个令牌,最多突发 40 个。这约束业务动作入口,不是所有 HTTP 接口或所有反向 RPC 的统一配额。 | 过载返回 | 超过并发、速率或队列限制返回 HTTP 429 / RATE_LIMITED,并带 Retry-After: 1;该请求未进入业务执行,可稍后使用原请求号重试。 | 执行超时 | 读请求执行超时返回 HTTP 504 / DEADLINE_EXCEEDED;已进入调用的写请求在超时或断连时保守返回 HTTP 504 / OUTCOME_UNKNOWN。排队时取消的请求未执行。失败实例退出正常调度并由既有监督机制恢复,旧实例失败不会停止替代它的新实例。 | 写请求不能因超时就换新请求号自动重发。先查询业务状态,或在应用支持幂等的前提下使用原 request_id 重试。底座记录服务的去重仍生效,但模块自己的多步骤流程和外部系统副作用需要各自的幂等与审计设计。请求取消不是数据库回滚,也不能保证外部操作已经终止。 验收包含竞争检测、跨模块重叠执行、同模块串行、账号及 API Key 隔离、过期回调、队列与速率限制、HTTP 状态、8 个现有 Docker 模块兼容,以及隔离容器压测。4 模块、4 客户端、每请求模拟 50 ms 等待的 32 次读取测试中,串行参考约 18 次/秒、并行约 75 次/秒,约 4.1 倍;该数字只说明此测试条件下消除了全局串行,不能作为真实业务吞吐或硬件容量承诺。复现入口为 cmd/concurrency-probe 和 cmd/concurrency-acceptance,必须使用独立测试数据,不能对生产资源做写入压测。 22. 运行监控(v0.14) 后台运行监控需要 monitor.read;系统管理员默认具备,支持独立监控角色及限权 Key。GET /api/monitoring 提供 JSON,GET /api/monitoring/metrics 提供 Prometheus text 0.0.4。/healthz 检查进程,/readyz 检查已注册且未主动停止的模块。X-Trace-ID 是服务端生成的定位号,区别于业务幂等 request_id。指标和最近 100 条请求仅保留在内存,重启清零;日志和指标不含正文、邮箱或密钥。容器资源按需采集并缓存 15 秒;当前不安装外部监控服务或发送告警。完整采集与口径说明:/docs/observability.md。 23. 模块自定义 JSON API(v0.15) manifest.api_routes 声明 name/method/path/action,入口 /apps//api。最多64条,路径不允许编码、点段、绝对URL或重叠模板;GET只能映射list。底座继续核验身份、权限与请求额度;req.API 提供可信路由元数据,路径变量绑定payload同名字段,冲突拒绝,查询通过req.API.Query读取。H5使用PlatformUI.request,Agent以Key调用;写入需X-Request-ID。只支持JSON,容器仍断网,不开放任意端口。完整规则:/docs/module-api.md。 24. 模块专属 PostgreSQL(v0.16) 声明 database:{engine:postgres} 与 database.connect,经审核后为应用分配独立账号和schema;Linux Docker专用Unix socket,连接配置仅给获批后端。每账号最多2连接,停止保留数据并停用账号,模拟使用独立测试schema。自有SQL的用户数据范围、事务、迁移与审计由应用接入,存量JSON不自动迁移。完整指南:/docs/module-database.md。