feat: harden community production foundation through story 1.5

This commit is contained in:
2026-08-14 00:21:59 +03:00
parent c30461cc92
commit f6fc2e5c9b
161 changed files with 16758 additions and 2515 deletions
+20
View File
@@ -23,7 +23,18 @@ Crank превращает REST API endpoint-ы в MCP-инструменты,
## Справочник
- [Capability Inventory](./capability-inventory.json) — machine-readable реестр
текущих и целевых flows; canonical path — `docs/capability-inventory.json`.
- [Schema Capability Inventory](./schemas/capability-inventory.schema.json)
- [Capability Baseline](./capability-baseline/manifest.json) — versioned SHA-256
snapshot фактических UI, Admin API и MCP flows, taxonomy, checklist и
sanitized evidence results.
- [Schema Capability Baseline](./schemas/capability-baseline.schema.json) —
fail-closed manifest contract.
- [Настройки окружения](./runtime-config.md)
- [Machine schema runtime-конфигурации](./schemas/runtime-config.schema.json)
- [Миграции PostgreSQL](./migrations.md)
- [Machine sequence миграций](./schemas/migration-sequence.json)
- [Admin API](./admin-api.md)
- [Развертывание](./deployment.md)
- [Production checklist](./production-checklist.md)
@@ -31,3 +42,12 @@ Crank превращает REST API endpoint-ы в MCP-инструменты,
- [Архитектура](./architecture.md)
- [Модель данных](./data-model.md)
- [Тестирование](./testing-strategy.md)
В Inventory только статус `implemented` означает готовность. Статусы `planned`,
`gap` и `blocked` остаются непроходными и не являются заявлением о наличии
функции в текущей Community-версии.
Baseline разделяет implementation status, execution verdict и evidence mode.
Полный pass — только `implemented + automated + pass`; `flaky`, `skipped`,
`not_run` и `manual_only` остаются non-pass. После изменения snapshot должны
обновиться `baseline_version` и manifest checksums.
+31
View File
@@ -0,0 +1,31 @@
{
"artifacts": [
{
"kind": "inventory",
"path": "docs/capability-inventory.json",
"sha256": "fa6728e211dc55bf49225884a6cf0182ecb2e3bc1dc59e4580499e551ae4dcf1"
},
{
"kind": "required_surfaces",
"path": "docs/capability-baseline/required-surfaces.json",
"sha256": "61714a2a141cf0a657816f35a6bfe31a7f2de77296303343bfaf6aeab9802681"
},
{
"kind": "taxonomy",
"path": "docs/capability-baseline/outcome-taxonomy.json",
"sha256": "2a57ef358ea08ad436bd96244cd7000a660e90b4ecd04a27d5fd071ea394e549"
},
{
"kind": "checklist",
"path": "docs/capability-baseline/manual-checklist.md",
"sha256": "688cafba282bac5a781f6b90f1a305d2ec30d1bf11b5a1f1af1f20fed3c26798"
},
{
"kind": "results",
"path": "docs/capability-baseline/results.json",
"sha256": "aa127c8e5aabed6bdc4fdcb4734da7ea1c4b2c82e42ff47c603737e2ffa38e58"
}
],
"baseline_version": "2026.08.10.1",
"schema_version": 1
}
@@ -0,0 +1,89 @@
# Community UI capability baseline checklist
baseline_version: 2026.08.10.1
This is a bounded brownfield baseline, not the release-candidate regression from Epic 8. For every charter record happy, loading, empty, error, recovery, stale-response, RU/EN, and safe-output observations. Use `not_run` for an applicable unexecuted state, `gap` for a missing required contract, and `n/a` only with a by-design reason.
## UI-01 Authentication and workspace
- flow_id: ui-auth-workspace
- states: happy, loading, error, recovery, stale, ru-en, safe-output
- verdict: pass
- reason: Wrong-password error and successful recovery were observed; settings and RU/EN navigation remained usable.
## UI-02 Operations catalog and lifecycle
- flow_id: ui-operation-lifecycle
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Happy, loading, empty, error and recovery rendered, but an older response overwrote newer state and a credential-shaped error canary reached the DOM; see DEF-UI-001 and DEF-UI-003.
## UI-03 Operation test execution
- flow_id: ui-operation-test
- states: happy, loading, error, recovery, stale, ru-en, safe-output
- verdict: pass
- reason: Wizard happy path, validation feedback and recovery controls were observed; loading and empty are not applicable to this local step flow.
## UI-04 YAML and OpenAPI import/export
- flow_id: ui-operation-import-export
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Import entry and recovery are present, but EN mode still exposes Russian-only OpenAPI text; see DEF-UI-002.
## UI-05 Agent lifecycle and bindings
- flow_id: ui-agent-management
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Happy, error and recovery rendered, but stale-response protection is absent and a credential-shaped error canary reached the DOM; see DEF-UI-001 and DEF-UI-003.
## UI-06 MCP and approval keys
- flow_id: ui-mcp-approval-keys
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: pass
- reason: Agent selection, key creation entry and single raw-key disclosure guidance were observed; the seeded stack made the initial empty state inapplicable.
## UI-07 Auth Profiles, Upstreams, and Secrets
- flow_id: ui-auth-profiles-upstreams-secrets
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Happy, real empty, error and recovery rendered, but a credential-shaped error canary reached the DOM; see DEF-UI-003.
## UI-08 Logs and approvals
- flow_id: ui-logs-approvals
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Happy, error and recovery rendered, but a credential-shaped error canary reached the DOM; see DEF-UI-003.
## UI-09 Usage
- flow_id: ui-usage
- states: happy, loading, empty, error, recovery, stale, ru-en, safe-output
- verdict: fail
- reason: Happy, error and recovery rendered, but a credential-shaped error canary reached the DOM; see DEF-UI-003.
## UI-10 Cross-screen stale response
- flow_id: ui-auth-workspace
- states: stale, recovery
- verdict: fail
- reason: A delayed older Operations response overwrote a newer empty response; see DEF-UI-001.
## UI-11 Localization consistency
- flow_id: ui-operation-lifecycle
- states: ru-en
- verdict: fail
- reason: Switching to EN left RU fragments on the Operations screen and OpenAPI import has hardcoded RU text; see DEF-UI-002.
## UI-12 Unsafe output canaries
- flow_id: ui-logs-approvals
- states: safe-output, error
- verdict: fail
- reason: Credential-shaped API error canaries were visible in multiple browser error states; see DEF-UI-003.
@@ -0,0 +1,28 @@
{
"baseline_version": "2026.08.10.1",
"evidence_modes": [
"automated",
"manual_only"
],
"execution_verdicts": [
"pass",
"fail",
"blocked",
"skipped",
"flaky",
"not_run"
],
"full_pass": {
"evidence_mode": "automated",
"execution_verdict": "pass",
"implementation_status": "implemented"
},
"implementation_statuses": [
"implemented",
"planned",
"gap",
"blocked"
],
"manual_only_rule": "Requires a retained manual result and next_evidence; it is not a full baseline pass.",
"non_pass_rule": "Failed, blocked, skipped, flaky, not_run, missing, or malformed evidence never becomes pass."
}
@@ -0,0 +1,95 @@
{
"baseline_version": "2026.08.10.1",
"required_flow_ids": [
"api-agent-catalog",
"api-approvals",
"api-auth-profiles-upstreams-secrets",
"api-auth-workspace",
"api-canonical-request-trace-identity",
"api-logs-usage",
"api-mcp-approval-keys",
"api-operation-import-export",
"api-operation-lifecycle",
"api-operation-test-run",
"mcp-approval-lifecycle",
"mcp-published-tool-call",
"mcp-published-tool-list",
"mcp-scoped-tool-search",
"mcp-transport-session",
"ui-agent-management",
"ui-auth-profiles-upstreams-secrets",
"ui-auth-workspace",
"ui-logs-approvals",
"ui-mcp-approval-keys",
"ui-operation-import-export",
"ui-operation-lifecycle",
"ui-operation-test",
"ui-usage"
],
"surface_groups": [
{
"flow_ids": [
"api-canonical-request-trace-identity"
],
"id": "correlation"
},
{
"flow_ids": [
"ui-operation-lifecycle",
"ui-operation-test",
"ui-operation-import-export",
"api-operation-lifecycle",
"api-operation-test-run",
"api-operation-import-export"
],
"id": "operations"
},
{
"flow_ids": [
"ui-agent-management",
"api-agent-catalog"
],
"id": "agents"
},
{
"flow_ids": [
"ui-auth-profiles-upstreams-secrets",
"api-auth-profiles-upstreams-secrets"
],
"id": "credentials"
},
{
"flow_ids": [
"ui-mcp-approval-keys",
"api-mcp-approval-keys",
"mcp-approval-lifecycle"
],
"id": "keys-and-approvals"
},
{
"flow_ids": [
"ui-logs-approvals",
"ui-usage",
"api-approvals",
"api-logs-usage"
],
"id": "history-and-usage"
},
{
"flow_ids": [
"ui-auth-workspace",
"api-auth-workspace"
],
"id": "auth-and-workspace"
},
{
"flow_ids": [
"mcp-transport-session",
"mcp-published-tool-list",
"mcp-published-tool-call",
"mcp-scoped-tool-search"
],
"id": "mcp-tools"
}
]
}
+318
View File
@@ -0,0 +1,318 @@
{
"baseline_version": "2026.08.10.1",
"defects": [
{
"contract": "Local Playwright stack starts only after the configured database exists and is stable.",
"flow_ids": ["ui-auth-workspace"],
"id": "DEF-BL-001",
"next_action": "Make readiness wait for the configured database after PostgreSQL bootstrap completes.",
"owner": "quality-community",
"severity": "Medium",
"steps": [
"Start the default Playwright stack with a new local PostgreSQL container.",
"Observe pg_isready succeed against the temporary bootstrap server.",
"Observe Admin API lose its connection when PostgreSQL restarts after database creation."
]
},
{
"contract": "Stopping the local Playwright stack terminates its loop and all child services.",
"flow_ids": ["ui-auth-workspace"],
"id": "DEF-BL-002",
"next_action": "Exit after the signal cleanup trap and terminate the actual cargo child processes.",
"owner": "quality-community",
"severity": "Medium",
"steps": [
"Start the Playwright stack and wait for readiness.",
"Send an interrupt to the stack process.",
"Observe the loop or re-parented Admin API and MCP processes remain alive."
]
},
{
"contract": "Workspace-dependent asynchronous UI loads reject stale responses.",
"flow_ids": [
"ui-agent-management",
"ui-auth-profiles-upstreams-secrets",
"ui-logs-approvals",
"ui-mcp-approval-keys",
"ui-operation-lifecycle",
"ui-usage"
],
"id": "DEF-UI-001",
"next_action": "Add request-generation identity guards in the owning UI hardening story and automate reordered responses.",
"owner": "admin-ui-community",
"severity": "Medium",
"steps": [
"Inspect workspace-change loaders for Operations, Agents, keys, secrets, logs, and usage.",
"Confirm requests are restarted on workspace change.",
"Confirm no shared request-generation or abort guard prevents an older response from overwriting newer state."
]
},
{
"contract": "OpenAPI import user-visible text follows the selected RU or EN locale.",
"flow_ids": ["ui-operation-import-export"],
"id": "DEF-UI-002",
"next_action": "Move OpenAPI import strings into the existing RU and EN i18n contract.",
"owner": "import-ui-community",
"severity": "Medium",
"steps": [
"Select the EN locale.",
"Open the OpenAPI import flow.",
"Observe Russian-only labels and status messages in the import component."
]
},
{
"contract": "Browser-visible error states never render credential-shaped fields received from an untrusted API response.",
"flow_ids": [
"ui-agent-management",
"ui-auth-profiles-upstreams-secrets",
"ui-logs-approvals",
"ui-operation-lifecycle",
"ui-usage"
],
"id": "DEF-UI-003",
"next_action": "Normalize browser API errors through a safe allowlisted message before rendering and add canary regressions for every async screen.",
"owner": "admin-ui-community",
"severity": "High",
"steps": [
"Authenticate to the local Community UI with seeded test data.",
"Return a bounded credential-shaped canary in a 500 response for each affected list endpoint.",
"Observe the untrusted response message rendered in the corresponding browser error state."
]
}
],
"environment_class": "community-test",
"manual_results": [
{
"check_id": "UI-01",
"evidence_mode": "manual_only",
"execution_verdict": "pass",
"flow_ids": ["ui-auth-workspace"],
"next_evidence": "Automate the observed login error, recovery and locale transitions."
},
{
"check_id": "UI-02",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-operation-lifecycle"],
"next_evidence": "Retest reordered responses and safe error rendering after DEF-UI-001 and DEF-UI-003 are fixed."
},
{
"check_id": "UI-03",
"evidence_mode": "manual_only",
"execution_verdict": "pass",
"flow_ids": ["ui-operation-test"],
"next_evidence": "Automate the observed wizard validation and recovery states."
},
{
"check_id": "UI-04",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-operation-import-export"],
"next_evidence": "Retest RU/EN after DEF-UI-002 and exercise malformed import recovery."
},
{
"check_id": "UI-05",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-agent-management"],
"next_evidence": "Retest reordered responses and safe error rendering after DEF-UI-001 and DEF-UI-003 are fixed."
},
{
"check_id": "UI-06",
"evidence_mode": "manual_only",
"execution_verdict": "pass",
"flow_ids": ["ui-mcp-approval-keys"],
"next_evidence": "Automate the observed empty/key-guidance state and a bounded create-and-cleanup reveal pass."
},
{
"check_id": "UI-07",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-auth-profiles-upstreams-secrets"],
"next_evidence": "Retest safe error rendering after DEF-UI-003 is fixed."
},
{
"check_id": "UI-08",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-logs-approvals"],
"next_evidence": "Retest safe error rendering after DEF-UI-003 is fixed."
},
{
"check_id": "UI-09",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-usage"],
"next_evidence": "Retest safe errors and reordered responses after DEF-UI-001 and DEF-UI-003 are fixed."
},
{
"check_id": "UI-10",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-auth-workspace"],
"next_evidence": "Add deterministic reordered-response tests after DEF-UI-001 is addressed."
},
{
"check_id": "UI-11",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-operation-lifecycle"],
"next_evidence": "Repeat the EN locale pass after DEF-UI-002 is addressed."
},
{
"check_id": "UI-12",
"evidence_mode": "manual_only",
"execution_verdict": "fail",
"flow_ids": ["ui-logs-approvals"],
"next_evidence": "Repeat the bounded canary pass after DEF-UI-003 is fixed."
}
],
"runs": [
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "rust-admin-integration",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"api-agent-catalog",
"api-approvals",
"api-auth-profiles-upstreams-secrets",
"api-auth-workspace",
"api-logs-usage",
"api-mcp-approval-keys",
"api-operation-import-export",
"api-operation-lifecycle",
"api-operation-test-run"
],
"id": "run-rust-admin-integration-fa07285c8e58",
"environment_class": "community-test",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"source_report_sha256": "fa07285c8e58dfad3834ffd420d80c91cdf0f0b5d0c54ef3e0eeaf35df3ab3f8"
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "rust-mcp-integration",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"mcp-approval-lifecycle",
"mcp-published-tool-call",
"mcp-published-tool-list",
"mcp-scoped-tool-search",
"mcp-transport-session"
],
"id": "run-rust-mcp-integration-ccff7c0029c1",
"environment_class": "community-test",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"source_report_sha256": "ccff7c0029c1347459a76b8ecd63a308a7162ddb5bc272441e79ffbf2ccdfb83"
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "ui-build",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"ui-auth-workspace",
"ui-mcp-approval-keys",
"ui-operation-import-export",
"ui-operation-test"
],
"id": "run-ui-build-755e91f2200b",
"environment_class": "community-test",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"source_report_sha256": "755e91f2200b4ac43783820afde4f5d6b336ff7a6ebd5e71b055c43d5c2e4986"
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "ui-playwright",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"ui-auth-workspace",
"ui-mcp-approval-keys",
"ui-operation-import-export",
"ui-operation-test"
],
"id": "run-ui-playwright-a1236d5f45ab",
"environment_class": "community-test",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"summary": {"passed": 21, "failed": 0, "flaky": 0, "skipped": 0},
"source_report_sha256": "a1236d5f45ab6273625bdb61634b1ea6c6d3ac876757b85a0a5ed8b5855fea6b"
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "authenticated-product-smoke",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"api-agent-catalog",
"api-mcp-approval-keys",
"api-operation-lifecycle",
"api-operation-test-run",
"mcp-published-tool-call",
"mcp-published-tool-list",
"mcp-transport-session"
],
"id": "run-authenticated-product-smoke-471a29ea400f",
"environment_class": "community-test",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"safe_outcome": {
"agent_id": "agent_019fe3a5468d75039020e367f1096136",
"agent_revision": 1,
"operation_id": "op_019fe3a546177533a2df26a36c79d358",
"operation_version": 1,
"stages": ["admin_test", "operation_publish", "agent_publish", "mcp_list", "mcp_call"]
},
"source_report_sha256": "471a29ea400fe1cb3eceb68d073c15ecba4e95ee5dff487fec0bb969f09c65e0"
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "just-verify",
"environment_class": "community-test",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"api-agent-catalog",
"api-approvals",
"api-auth-profiles-upstreams-secrets",
"api-auth-workspace",
"api-logs-usage",
"api-mcp-approval-keys",
"api-operation-import-export",
"api-operation-lifecycle",
"api-operation-test-run",
"mcp-approval-lifecycle",
"mcp-published-tool-call",
"mcp-published-tool-list",
"mcp-scoped-tool-search",
"mcp-transport-session"
],
"id": "run-just-verify-c6c16ce3c4b2",
"source_report_sha256": "c6c16ce3c4b2fa8355ae73c6ad6e87f3c829e86d720cd153133c161d073330b2",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"summary": {"exit_code": 0, "skipped": 0, "timed_out": false}
},
{
"accepted": true,
"collector": "capability-baseline-collector-v1",
"command_id": "just-verify",
"environment_class": "community-test",
"evidence_mode": "automated",
"execution_verdict": "pass",
"flow_ids": [
"api-canonical-request-trace-identity"
],
"id": "run-just-verify-a3e1538374ee",
"source_report_sha256": "a3e1538374eea02b3140ea20a7966c01225a78ac510296d0bb08eaf87a3c476c",
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006",
"summary": {"exit_code": 0, "skipped": 0, "timed_out": false}
}
],
"source_revision": "c30461cc92491ebe3d527e125a1657b73fa67006"
}
+316
View File
@@ -0,0 +1,316 @@
{
"flows": [
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/login.spec.js", "apps/ui/tests/e2e/workspace-settings.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-auth-workspace",
"owner": "admin-ui-community",
"requirements": ["FR-3", "FR-46"],
"status": "implemented",
"type": "ui",
"user_outcome": "An administrator can authenticate and work in the selected Community workspace."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/operations.spec.js", "apps/ui/tests/e2e/wizard.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-operation-lifecycle",
"owner": "operations-ui-community",
"requirements": ["FR-1", "FR-46"],
"status": "blocked",
"type": "ui",
"user_outcome": "An administrator can create, edit, publish, archive, and inspect REST Operations."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/wizard.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-operation-test",
"owner": "runtime-ui-community",
"requirements": ["FR-2", "FR-46"],
"status": "implemented",
"type": "ui",
"user_outcome": "An administrator can execute a bounded Operation test and inspect its safe outcome."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/operations.spec.js", "apps/ui/tests/e2e/wizard.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-operation-import-export",
"owner": "import-ui-community",
"requirements": ["FR-1", "FR-46"],
"status": "implemented",
"type": "ui",
"user_outcome": "An administrator can import OpenAPI or YAML drafts and export an Operation configuration."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/agents.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-agent-management",
"owner": "agents-ui-community",
"requirements": ["FR-4", "FR-46"],
"status": "blocked",
"type": "ui",
"user_outcome": "An administrator can manage an Agent and its published Operation bindings."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/api-keys.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-mcp-approval-keys",
"owner": "keys-ui-community",
"requirements": ["FR-3", "FR-5", "FR-46"],
"status": "implemented",
"type": "ui",
"user_outcome": "An administrator can create, reveal once, inspect, revoke, and delete MCP or approval keys."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/secrets.spec.js", "apps/ui/tests/e2e/wizard.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-auth-profiles-upstreams-secrets",
"owner": "credentials-ui-community",
"requirements": ["FR-3", "FR-46"],
"status": "blocked",
"type": "ui",
"user_outcome": "An administrator can configure reusable upstream, Auth Profile, and secret references without re-reading plaintext."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/logs-usage.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-logs-approvals",
"owner": "history-ui-community",
"requirements": ["FR-5", "FR-6", "FR-46"],
"status": "blocked",
"type": "ui",
"user_outcome": "An administrator can inspect invocation logs, safe details, and pending approvals."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/ui/tests/e2e/logs-usage.spec.js"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "ui-usage",
"owner": "usage-ui-community",
"requirements": ["FR-6", "FR-46"],
"status": "blocked",
"type": "ui",
"user_outcome": "An administrator can inspect workspace, Operation, and Agent usage summaries."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/auth_rate_limit.rs", "apps/admin-api/tests/integration/community_access_usage.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-auth-workspace",
"owner": "admin-api-community",
"requirements": ["FR-3", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Authenticated Admin API requests are scoped to the authorized Community workspace."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/operations_agents.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-operation-lifecycle",
"owner": "operations-api-community",
"requirements": ["FR-1", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can manage immutable published Operation versions and drafts."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/operations_agents.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-operation-test-run",
"owner": "runtime-api-community",
"requirements": ["FR-2", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can execute a REST Operation test through the current runtime boundary."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/openapi_import.rs", "apps/admin-api/tests/integration/secrets_import_auth.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-operation-import-export",
"owner": "import-api-community",
"requirements": ["FR-1", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can preview and create OpenAPI imports and round-trip YAML configuration."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/operations_agents.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-agent-catalog",
"owner": "agents-api-community",
"requirements": ["FR-4", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can manage Agent revisions, exact bindings, publication, and catalog search policy."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/community_access_usage.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-mcp-approval-keys",
"owner": "keys-api-community",
"requirements": ["FR-3", "FR-5", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can manage separately scoped MCP and approval keys with a single raw disclosure."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/secrets_import_auth.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-auth-profiles-upstreams-secrets",
"owner": "credentials-api-community",
"requirements": ["FR-3", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can manage upstreams, Auth Profiles, and encrypted secret references."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/community_access_usage.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-approvals",
"owner": "approvals-api-community",
"requirements": ["FR-5", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can list and inspect approval requests without leaking unsafe payloads."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/community_access_usage.rs", "apps/admin-api/tests/dc08.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "api-logs-usage",
"owner": "history-api-community",
"requirements": ["FR-6", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin API clients can query safe invocation history and usage summaries."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/mcp-server/tests/integration/transport_protocol.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "mcp-transport-session",
"owner": "mcp-transport-community",
"requirements": ["FR-3", "FR-4", "FR-46"],
"status": "implemented",
"type": "mcp",
"user_outcome": "An authorized MCP client can initialize and terminate a bounded Streamable HTTP session."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/mcp-server/tests/integration/transport_protocol.rs", "apps/mcp-server/tests/integration/catalog_access.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "mcp-published-tool-list",
"owner": "mcp-catalog-community",
"requirements": ["FR-4", "FR-46"],
"status": "implemented",
"type": "mcp",
"user_outcome": "An MCP client sees only bound, authorized, published Tool versions for its Agent."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/mcp-server/tests/integration/transport_protocol.rs", "apps/mcp-server/tests/integration/execution_stages.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "mcp-published-tool-call",
"owner": "mcp-runtime-community",
"requirements": ["FR-2", "FR-4", "FR-6", "FR-46"],
"status": "implemented",
"type": "mcp",
"user_outcome": "An MCP client can invoke an authorized published REST Tool and receive a safe structured outcome."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/mcp-server/tests/integration/tool_search.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "mcp-scoped-tool-search",
"owner": "mcp-catalog-community",
"requirements": ["FR-4", "FR-46"],
"status": "implemented",
"type": "mcp",
"user_outcome": "An MCP client can use Agent-scoped search meta-tools without escaping its published catalog."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/mcp-server/tests/integration/catalog_access/approval_access.rs"], "manual": ["docs/capability-baseline/manual-checklist.md"]},
"id": "mcp-approval-lifecycle",
"owner": "mcp-approvals-community",
"requirements": ["FR-5", "FR-46"],
"status": "implemented",
"type": "mcp",
"user_outcome": "A separately authorized approval client can list and decide pending requests with bounded side effects."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["apps/admin-api/tests/integration/request_context.rs", "apps/mcp-server/tests/integration/request_context.rs", "apps/mcp-server/tests/integration/execution_stages.rs", "crates/crank-registry/tests/integration/migrations.rs"], "manual": ["docs/observability.md"]},
"id": "api-canonical-request-trace-identity",
"owner": "observability-community",
"requirements": ["FR-27", "FR-46"],
"status": "implemented",
"type": "api",
"user_outcome": "Admin and MCP requests expose separate safe Request and Trace identities that survive runtime execution and history persistence without requiring telemetry export."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-production-foundation",
"owner": "platform-community",
"requirements": ["FR-7", "FR-8", "FR-9", "FR-10", "FR-11", "FR-12", "FR-13", "FR-14", "FR-28", "FR-29", "FR-30", "FR-31", "FR-32", "FR-33", "FR-34", "FR-35"],
"status": "planned",
"type": "api",
"user_outcome": "Community runtime, safety, correlation, and observability foundations reach their planned production contracts."
},
{
"capabilities": ["resources"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-resource-read",
"owner": "mcp-community",
"requirements": ["FR-15", "FR-16", "FR-46"],
"status": "planned",
"type": "mcp",
"user_outcome": "MCP clients can discover and read published Resources."
},
{
"capabilities": ["prompts"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-prompt-get",
"owner": "mcp-community",
"requirements": ["FR-17", "FR-18", "FR-19", "FR-46"],
"status": "planned",
"type": "mcp",
"user_outcome": "MCP clients can list and render published parameterized Prompts."
},
{
"capabilities": ["tasks"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-background-task",
"owner": "execution-community",
"requirements": ["FR-20", "FR-21", "FR-22", "FR-23", "FR-24", "FR-25", "FR-26", "FR-46"],
"status": "planned",
"type": "mcp",
"user_outcome": "Users can observe and control durable background executions."
},
{
"capabilities": ["load_runs"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-load-run",
"owner": "quality-community",
"requirements": ["FR-36", "FR-37", "FR-38", "FR-39", "FR-40", "FR-41", "FR-46"],
"status": "planned",
"type": "ui",
"user_outcome": "Administrators can run a bounded load scenario and inspect its quality statistics."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-community-deployment",
"owner": "release-community",
"requirements": ["FR-42", "FR-43", "FR-44", "FR-45", "FR-46"],
"status": "planned",
"type": "api",
"user_outcome": "Operators can deploy, upgrade, restore, and operate a production Community installation."
},
{
"capabilities": ["tools"],
"evidence": {"automated": ["tests/unit/test_validate_capability_inventory.py"], "manual": ["docs/intro.md"]},
"id": "planned-release-qualification",
"owner": "quality-community",
"requirements": ["FR-47", "FR-48", "FR-49", "FR-50", "FR-51", "FR-52", "FR-53", "FR-54"],
"status": "planned",
"type": "api",
"user_outcome": "A release candidate is qualified by immutable, reproducible technical and manual evidence."
}
],
"product": "crank-community",
"schema_version": 1
}
+10 -7
View File
@@ -2,11 +2,12 @@
Документ описывает поддерживаемый путь запуска Crank на сервере.
Crank запускается как три контейнера за reverse proxy:
Crank запускает три long-running контейнера за reverse proxy и один обязательный one-shot migration job:
- `ui`
- `admin-api`
- `mcp-server`
- `migrate` (завершается до readiness Admin/MCP)
Можно использовать внешний PostgreSQL или локальный PostgreSQL из compose-профиля `local-db`.
@@ -18,8 +19,10 @@ reverse proxy
/api/admin/ -> admin-api:3001
/mcp/ -> mcp-server:3002
admin-api -> PostgreSQL
mcp-server -> PostgreSQL
migrate -> PostgreSQL -> exit 0
|
+-> admin-api readiness
+-> mcp-server readiness
admin-api -> Valkey/Redis, опционально
mcp-server -> Valkey/Redis, опционально
admin-api -> внешний OTLP endpoint, опционально
@@ -105,7 +108,7 @@ docker compose \
config -q
```
Запуск с внешним PostgreSQL:
Перед обновлением существующей установки выполните preflight и проверенный backup по [migration runbook](migrations.md). Source Compose по умолчанию поднимает локальный PostgreSQL 16. После безопасной последовательности запустите:
```bash
docker compose \
@@ -143,7 +146,7 @@ cp .env.example .env
docker compose --profile local-db up -d
```
Если используется внешний PostgreSQL, заполните `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` и запустите:
Если используется внешний PostgreSQL, заполните `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, сначала выполните preflight и backup по [migration runbook](migrations.md), затем запустите:
```bash
docker compose up -d
@@ -182,9 +185,9 @@ curl -I http://127.0.0.1:3000/
CRANK_RESTORE_CONFIRM=restore ./scripts/restore-community.sh /opt/crank /opt/crank/backups/20260721T120000Z
```
- Обновления схемы выполняются под блокировкой, одной транзакцией и фиксируются в `__crank_core_migrations`. Миграции Community должны оставаться обратно совместимыми с предыдущей версией приложения.
- Обновления схемы выполняет только one-shot `crank-migrate apply` под canonical transaction advisory lock. `admin-api` и `mcp-server` выполняют read-only compatibility check и не стартуют до успешного migration job. Canonical sequence фиксируется в `__crank_migrations`, legacy ledgers остаются readable; подробности — в [migrations.md](migrations.md).
- Не храните реальные секреты в Git.
- CD использует неизменяемые теги коммитов и автоматически возвращает прежнюю конфигурацию и образы при провале readiness.
- CD использует неизменяемые теги коммитов. После schema migration автоматический возврат старых образов запрещён, пока N/N-1 window не квалифицирован Story 7.2: оператор сохраняет backup и выбирает matching forward image либо доказанное восстановление согласованного комплекта.
- `CRANK_PUBLISH_BIND=0.0.0.0` нужен только если reverse proxy работает на другом host.
- OTLP Collector и хранилище трасс не входят в Community Compose. Для
внешнего приёмника задайте `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`; секретные
+16
View File
@@ -27,6 +27,19 @@ This repository contains the Community version:
- PostgreSQL database;
- optional Valkey or Redis for temporary coordination state.
The executable Community boundary is recorded in
[`docs/capability-inventory.json`](../capability-inventory.json). Only
`implemented` denotes a delivered flow; `planned`, `gap`, and `blocked` do not
count as ready. Resources, Prompts, background Tasks, and Load Runs are planned
targets and are not advertised as implemented in the current version.
The verified brownfield snapshot is recorded in
[`docs/capability-baseline/manifest.json`](../capability-baseline/manifest.json).
It binds the inventory, required surfaces, taxonomy, bounded manual checklist,
and sanitized results by exact SHA-256. A full pass requires
`implemented + automated + pass`; flaky, skipped, not-run, and manual-only
evidence remain non-pass.
## Documentation
The main documentation is currently maintained in Russian:
@@ -38,6 +51,9 @@ The main documentation is currently maintained in Russian:
- [MCP interface](../mcp-interface.md)
- [Admin API](../admin-api.md)
- [Runtime configuration](../runtime-config.md)
- [Runtime configuration machine schema](../schemas/runtime-config.schema.json)
- [PostgreSQL migration contract](migrations.md) ([full operator contract in Russian](../migrations.md))
- [Migration machine sequence](../schemas/migration-sequence.json)
- [Troubleshooting](../troubleshooting.md)
## License
+22
View File
@@ -0,0 +1,22 @@
# PostgreSQL migrations
Crank has one append-only migration authority in `crank-registry`. The Admin and MCP services run a read-only compatibility preflight; only the one-shot `crank-migrate` process may execute DDL.
For an existing installation, always run: read-only `preflight`, verified PostgreSQL and artifact-storage backup, `plan --check`, controlled `apply`, then a final `preflight`. Exit code `2` from CLI preflight means migration is required; exit code `1` blocks mutation until the reported condition is resolved.
Source command:
```bash
cargo run -p admin-api --bin crank-migrate -- plan --check
docker compose -f deploy/community/docker-compose.yml \
--env-file deploy/community/.env.example \
run --rm migrate crank-migrate preflight
```
The migrator reads only database configuration and retries connection for a bounded startup window. Diagnostics contain stable `code`, `stage`, nullable `version`, and `recovery`, never raw SQL, driver output, database URLs, credentials, or row data.
Published migrations are immutable and checksummed. Partial schema, unknown extension provenance, checksum drift, and future versions fail closed. Automatic down migrations, `--force`, destructive rollback, and arbitrary SQL input are not supported. Full N/N-1 upgrade and rollback qualification belongs to Story 7.2.
Version 3 adds nullable canonical Trace ID storage and partial Request/Trace indexes. New application writes provide both identities; historical rows remain honestly nullable and are never assigned fabricated traces.
For the complete ledger inventory, recovery table, and authoring rules, see the canonical [Russian operator contract](../migrations.md).
+16 -1
View File
@@ -36,6 +36,22 @@ MCP-клиент
- PostgreSQL как основное хранилище;
- опциональный Valkey или Redis для служебного кэша.
Исполняемая граница Community описана в
[`docs/capability-inventory.json`](./capability-inventory.json). Только
`implemented` означает доступную возможность; `planned`, `gap` и `blocked` не
считаются готовностью. Resources, Prompts, фоновые Tasks и Load Runs пока
являются planned target, а не возможностями текущей версии.
Фактический brownfield baseline закреплён в
[`docs/capability-baseline/manifest.json`](./capability-baseline/manifest.json).
Manifest проверяет SHA-256 inventory, required surfaces, taxonomy, bounded
manual checklist и sanitized results. Flaky, skipped, not-run и manual-only
evidence не считаются полным pass; дефекты остаются связаны с flow и owner.
PostgreSQL schema изменяет только controlled one-shot команда. Admin API и MCP
при startup выполняют read-only compatibility check; операторский порядок и
failure contract описаны в [`docs/migrations.md`](./migrations.md).
## Демо при первом запуске
В примерах окружения включен `CRANK_DEMO_SEED=true`. После первого запуска Crank создает:
@@ -47,4 +63,3 @@ MCP-клиент
- пример записи в журнале вызовов.
Демо можно отключить, указав `CRANK_DEMO_SEED=false`.
+3 -1
View File
@@ -274,4 +274,6 @@ CRANK_MCP_REFRESH_MS=5000
- `400 Bad Request` - неверный MCP-заголовок, session id или JSON-RPC payload.
- `429 Too Many Requests` - сработал rate limit.
Runtime-ошибки инструмента возвращаются как структурированный MCP tool error с кодом, сообщением и `request_id`.
Runtime-ошибки инструмента возвращаются как структурированный MCP tool error с кодом,
сообщением, `request_id` и отдельным `trace_id`. Транспортные ответы также содержат
`x-request-id` и `x-trace-id`; отклонённые входные значения в них не отражаются.
+80
View File
@@ -0,0 +1,80 @@
# Миграции PostgreSQL
Crank использует единственную append-only migration authority в `crank-registry`. `admin-api` и `mcp-server` выполняют только read-only preflight. DDL применяет one-shot binary `crank-migrate`, который Community Compose запускает до readiness сервисов.
## Безопасная последовательность оператора
Для source Compose замените `<compose>` на:
```bash
docker compose -f deploy/community/docker-compose.yml --env-file deploy/community/.env.example
```
Для image Compose используйте `docker compose -f deploy/community/docker-compose.images.yml --env-file .env`. Перед обновлением уже работающей установки выполните строго:
1. Read-only preflight: `<compose> run --rm migrate crank-migrate preflight`. Exit `0` означает current, exit `2` — ожидаемую `migration_required`; exit `1` запрещает mutation до устранения причины.
2. Создайте и проверьте согласованный backup PostgreSQL и artifact storage. Для первой пустой установки зафиксируйте, что восстанавливать нечего.
3. Проверьте immutable plan: `cargo run -p admin-api --bin crank-migrate -- plan --check` в source checkout либо `<compose> run --rm migrate crank-migrate plan` для образа.
4. Примените sequence: `<compose> run --rm migrate crank-migrate apply`.
5. Повторите preflight и убедитесь в `{"status":"current","version":3}`.
6. Только теперь запускайте long-running services: `<compose> up -d`.
Обычный `up` также содержит обязательный migration job, но при upgrade он не заменяет предварительные preflight и backup. Migrator делает до десяти bounded попыток подключения с секундной паузой и затем безопасно завершается ошибкой.
Команда читает только `CRANK_DATABASE_URL`/`POSTGRES_*`. Master key, session secret, bootstrap password, MCP credentials и другие service secrets не входят в её config projection.
## Фактический brownfield inventory
| Источник | Исторический владелец/lock | Известный контракт |
|---|---|---|
| `__crank_core_migrations` | `crank-registry`, session lock `0x4352414e4b` | columns `version, description, checksum, applied_at`; v1 checksum `crank-community-baseline-v1` |
| `__crank_mcp_migrations` | MCP session store, session lock `0x4352414e4b4d4350` | columns `version, checksum, applied_at`; v1 checksum `mcp-transport-sessions-v1` |
| `__crank_ext_migrations` | прежний `RegistryExtension`, без общего lock | legacy columns `extension_name, version, applied_at`; checksum отсутствовал |
| `__crank_migrations` | canonical `MigrationAuthority`, transaction lock `0x4352414e4b4d4947` | append-only sequence с version/name/checksum/phase/compatibility |
| `__crank_migration_legacy_audit` | canonical `MigrationAuthority` | только доказуемо сопоставленное legacy provenance |
Legacy extension row без зарегистрированного exact `(name, version, checksum)` несовместим. Community пока не публиковала extension migrations, поэтому authority не выдумывает им checksum и блокирует такие строки с `legacy_conflict`.
## Контракт sequence
Machine plan находится в [`schemas/migration-sequence.json`](schemas/migration-sequence.json) и проверяется командой:
```bash
cargo run -p admin-api --bin crank-migrate -- plan --check
```
- V1 — immutable brownfield baseline с историческим ledger token и отдельным exact-source SHA-256.
- V2 — единый exact-byte expand SQL artifact, создающий canonical ledgers и MCP session schema; его SHA-256 закреплён в executable descriptor.
- V3 — append-only expand для независимого nullable `invocation_logs.trace_id`, canonical-format constraint и partial request/trace indexes; исторические строки остаются `NULL` без fabricated backfill.
- Каждая версия имеет contiguous `i64` version, стабильное имя, lowercase SHA-256, owner, phase, explicit readable schema min/max и backfill policy.
- `migrate` требует bounded cursor/batch policy; `contract` дополнительно требует tracked compatibility evidence и закрытого окна.
- Добавление descriptor без executable implementation блокируется `invalid_contract` до DB I/O.
Legacy ledgers остаются readable. Down migration, destructive automatic rollback, ledger rewrite, `--force` и arbitrary SQL/path input отсутствуют. Полная N/N-1 upgrade/rollback qualification остаётся Story 7.2.
## Диагностика и восстановление
CLI/stderr возвращают bounded JSON: `code`, `stage`, nullable numeric `version`, `recovery`. Database URL, credentials, raw SQL/driver body, row values и host paths не выводятся.
| Code | Значение | Безопасное действие |
|---|---|---|
| `schema_missing` | Service startup увидел пустую schema | Запустить controlled migration; не разрешать runtime DDL |
| `migration_required` | Schema отстаёт; preflight CLI завершает работу с exit `2` | Проверить backup и выполнить controlled `apply` |
| `checksum_mismatch` | Artifact и ledger не совпали | Сначала проверить immutable application image/release manifest; восстанавливать БД только после доказанной ledger corruption |
| `partial_sequence` | Relation/ledger/structural fingerprint неполон | Не чинить вручную; сопоставить backup и matching artifact |
| `future_version` | База новее приложения | Установить matching application; не откатывать schema автоматически |
| `legacy_conflict` | Legacy provenance невозможно доказать | Сохранить backup и привлечь оператора |
| `lock_timeout` | Другой migration runner удерживает canonical lock | Дождаться завершения и повторить preflight |
| `apply_failed` | Transaction migration откатилась | Проверить matching artifact/backup, затем повторить preflight |
| `storage_unavailable` | PostgreSQL/transport недоступен | Проверить сеть/TLS/права; секреты в diagnostic не копировать |
| `config_invalid` | Database-only config невалиден | Исправить указанный config contract |
| `invalid_command` | Неизвестная CLI команда/аргумент | Использовать только `plan`, `preflight`, `apply` |
| `contract_drift` | Committed machine plan расходится с Rust authority | Перегенерировать только для новой append-only version и проверить diff |
| `invalid_contract` | Descriptor/implementation/window/evidence несовместимы | Исправить authoring contract до любого DB I/O |
## Правила разработчика
- DDL, SQL migration assets и canonical advisory lock разрешены только в `crank-registry::migrations`.
- Schema развивается `expand → bounded/resumable migrate → evidence-gated contract`.
- Опубликованные source bytes/checksums не редактируются: добавляется новая version.
- Для каждой version обязательны fresh/current/concurrent/corrupt/rollback/data-preservation tests и explicit Community scope scan.
+20 -12
View File
@@ -15,6 +15,7 @@
"target": "admin_api::request_context",
"event": "admin.request.completed",
"request_id": "019...",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"fields": {
"method": "GET",
"route": "/api/operations",
@@ -38,14 +39,14 @@ Admin API и MCP принимают `x-request-id` как непрозрачны
отсутствует или содержит пробелы, управляющие символы, `,`, `;`, не-ASCII
символы либо больше 128 байт, Crank создаёт UUIDv7.
Один идентификатор:
Request ID и отдельный Trace ID:
- возвращается в `x-request-id` успешного или ошибочного ответа;
- записывается в корневой span входного запроса;
- передаётся в runtime;
- возвращаются в `x-request-id`/`x-trace-id` успешного или ошибочного ответа;
- записываются в корневой span входного запроса;
- типизированно передаются в runtime;
- заменяет статические или полученные из mapping значения
`x-request-id` и `x-correlation-id` перед исходящим REST-запросом;
- сохраняется в прикладной истории вызова.
- сохраняются в прикладной истории вызова.
`request id` не является `trace id` и не подменяет распределённую
трассировку.
@@ -214,15 +215,22 @@ series и расход памяти измеряются в истории 1.8;
## Распределённые трассы
Crank принимает и передаёт стандартный W3C `traceparent` на границах Admin
API, MCP и исходящих REST-вызовов. `x-request-id` остаётся отдельным
идентификатором запроса. Baggage не извлекается и не передаётся.
API, MCP и исходящих REST-вызовов. Ровно один canonical parent принимается;
duplicate, malformed, uppercase и zero-ID значения заменяются новым local
trace без echo входа. `x-request-id` остаётся отдельным идентификатором
запроса, а `x-trace-id` — безопасным локальным support ID. Caller
`tracestate` ограничен 512 байтами/32 members, baggage — 8192 байтами/64
members; Community allowlist пуста, поэтому они не извлекаются и не
передаются.
Экспорт отключён по умолчанию: при пустых
`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` и `OTEL_EXPORTER_OTLP_ENDPOINT`
provider, batch processor и фоновый поток не создаются. После включения
используется только OTLP/HTTP binary protobuf. Community Compose не включает
Collector или хранилище трасс: оператор подключает внешний совместимый
приёмник.
`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` и `OTEL_EXPORTER_OTLP_ENDPOINT` local
provider сохраняет span topology и Trace ID, но exporter, batch processor,
фоновая очередь и сетевой трафик не создаются. Поэтому response, runtime и
Invocation History получают Trace ID независимо от sampling/export. После
включения используется только OTLP/HTTP binary protobuf. Community Compose
не включает Collector или хранилище трасс: оператор подключает внешний
совместимый приёмник.
Уровень эксплуатационных журналов не отключает трассы. В OTLP попадают
только явно отмеченные spans с внутренней целью `crank::trace`; события
+81 -4
View File
@@ -2,6 +2,70 @@
Crank настраивается через переменные окружения. Один и тот же набор переменных используется при запуске из исходников и при запуске готовых Docker-образов.
<!-- BEGIN GENERATED CRANK RUNTIME CONFIG -->
| Environment | Semantic path | Process | Type/unit | Default | Bounds | Sensitivity | Mode |
|---|---|---|---|---|---|---|---|
| `CRANK_DATABASE_URL` | `database.url` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` |
| `POSTGRES_HOST` | `database.host` | `Shared` | `string/-` | `postgres` | `-` | `Internal` | `Effective` |
| `POSTGRES_PORT` | `database.port` | `Shared` | `u16/port` | `5432` | `1..=65535` | `Public` | `Effective` |
| `POSTGRES_DB` | `database.name` | `Shared` | `string/-` | `crank` | `-` | `Internal` | `Effective` |
| `POSTGRES_USER` | `database.user` | `Shared` | `string/-` | `crank` | `-` | `Internal` | `Effective` |
| `POSTGRES_PASSWORD` | `database.password` | `Shared` | `secret/-` | `configured` | `-` | `Secret` | `Effective` |
| `POSTGRES_MAX_CONNECTIONS` | `database.pool.max_connections` | `Shared` | `u32/connections` | `20` | `1..=1024` | `Public` | `Effective` |
| `POSTGRES_MIN_CONNECTIONS` | `database.pool.min_connections` | `Shared` | `u32/connections` | `2` | `0..=1024` | `Public` | `Effective` |
| `POSTGRES_ACQUIRE_TIMEOUT_MS` | `database.pool.acquire_timeout_ms` | `Shared` | `u64/milliseconds` | `5000` | `1..=300000` | `Public` | `Effective` |
| `POSTGRES_IDLE_TIMEOUT_MS` | `database.pool.idle_timeout_ms` | `Shared` | `u64/milliseconds` | `600000` | `1000..=86400000` | `Public` | `Effective` |
| `POSTGRES_MAX_LIFETIME_MS` | `database.pool.max_lifetime_ms` | `Shared` | `u64/milliseconds` | `1800000` | `1000..=86400000` | `Public` | `Effective` |
| `CRANK_MASTER_KEY` | `runtime.master_key` | `Shared` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` |
| `CRANK_BASE_URL` | `runtime.base_url` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` |
| `CRANK_RUNTIME_MAX_CONCURRENT_UNARY` | `runtime.max_concurrent_unary` | `Shared` | `u32/requests` | `64` | `1..=65535` | `Public` | `Effective` |
| `CRANK_CACHE_BACKEND` | `cache.backend` | `Shared` | `enum/-` | `memory` | `-` | `Public` | `Effective` |
| `CRANK_CACHE_URL` | `cache.url` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` |
| `CRANK_CACHE_DEFAULT_TTL_MS` | `cache.default_ttl_ms` | `Shared` | `u64/milliseconds` | `blank` | `1..=86400000` | `Public` | `DeprecatedNoEffect` |
| `CRANK_OUTBOUND_ALLOWED_HOSTS` | `outbound.allowed_hosts` | `Shared` | `host_list/-` | `` | `-` | `Internal` | `Effective` |
| `CRANK_OUTBOUND_DENIED_HOSTS` | `outbound.denied_hosts` | `Shared` | `host_list/-` | `` | `-` | `Internal` | `Effective` |
| `CRANK_OUTBOUND_MAX_RESPONSE_BYTES` | `outbound.max_response_bytes` | `Shared` | `u64/bytes` | `4194304` | `1..=67108864` | `Public` | `Effective` |
| `CRANK_ENVIRONMENT` | `observability.environment` | `Shared` | `label/-` | `development` | `-` | `Public` | `Effective` |
| `CRANK_LOG_LEVEL` | `observability.log_filter` | `Shared` | `string/-` | `blank` | `-` | `Public` | `Effective` |
| `CRANK_SENTRY_DSN` | `observability.sentry_dsn` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` |
| `CRANK_METRICS_ENABLED` | `observability.metrics.enabled` | `Shared` | `bool/-` | `true` | `-` | `Public` | `Effective` |
| `CRANK_METRICS_BEARER_TOKEN` | `observability.metrics.bearer_token` | `Shared` | `secret/-` | `blank` | `-` | `Secret` | `Effective` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `observability.otlp.endpoint` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `observability.otlp.traces_endpoint` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `observability.otlp.protocol` | `Shared` | `enum/-` | `http/protobuf` | `-` | `Public` | `Effective` |
| `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | `observability.otlp.traces_protocol` | `Shared` | `enum/-` | `blank` | `-` | `Public` | `Effective` |
| `OTEL_EXPORTER_OTLP_TIMEOUT` | `observability.otlp.timeout` | `Shared` | `duration/milliseconds` | `10000` | `1..=300000` | `Public` | `Effective` |
| `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `observability.otlp.traces_timeout` | `Shared` | `duration/milliseconds` | `blank` | `1..=300000` | `Public` | `Effective` |
| `OTEL_EXPORTER_OTLP_HEADERS` | `observability.otlp.headers` | `Shared` | `headers/-` | `blank` | `-` | `Secret` | `Effective` |
| `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `observability.otlp.traces_headers` | `Shared` | `headers/-` | `blank` | `-` | `Secret` | `Effective` |
| `OTEL_BSP_MAX_QUEUE_SIZE` | `observability.otlp.max_queue_size` | `Shared` | `u32/spans` | `2048` | `1..=65536` | `Public` | `Effective` |
| `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | `observability.otlp.max_export_batch_size` | `Shared` | `u32/spans` | `512` | `1..=65536` | `Public` | `Effective` |
| `OTEL_BSP_SCHEDULE_DELAY` | `observability.otlp.schedule_delay` | `Shared` | `duration/milliseconds` | `5000` | `1..=300000` | `Public` | `Effective` |
| `OTEL_BSP_EXPORT_TIMEOUT` | `observability.otlp.export_timeout` | `Shared` | `duration/milliseconds` | `30000` | `1..=300000` | `Public` | `Effective` |
| `CRANK_ADMIN_BIND` | `admin.bind` | `AdminApi` | `socket/-` | `0.0.0.0:3001` | `-` | `Internal` | `Effective` |
| `CRANK_ADMIN_METRICS_BIND` | `admin.metrics_bind` | `AdminApi` | `socket/-` | `127.0.0.1:9464` | `-` | `Internal` | `Effective` |
| `CRANK_STORAGE_ROOT` | `admin.storage_root` | `AdminApi` | `absolute_path/-` | `/var/lib/crank/storage` | `-` | `Internal` | `Effective` |
| `CRANK_ADMIN_RATE_LIMIT_RPS` | `admin.rate_limit.rps` | `AdminApi` | `u32/requests_per_second` | `30` | `1..=100000` | `Public` | `Effective` |
| `CRANK_ADMIN_RATE_LIMIT_BURST` | `admin.rate_limit.burst` | `AdminApi` | `u32/requests` | `60` | `1..=1000000` | `Public` | `Effective` |
| `CRANK_INVOCATION_LOG_RETENTION_DAYS` | `admin.invocation_log_retention_days` | `AdminApi` | `u32/days` | `30` | `1..=36500` | `Public` | `Effective` |
| `CRANK_SESSION_SECRET` | `admin.session.secret` | `AdminApi` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` |
| `CRANK_PASSWORD_PEPPER` | `admin.password_pepper` | `AdminApi` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` |
| `CRANK_SESSION_TTL_HOURS` | `admin.session.ttl_hours` | `AdminApi` | `u32/hours` | `24` | `1..=8760` | `Public` | `Effective` |
| `CRANK_TRUST_FORWARDED_HEADERS` | `admin.trust_forwarded_headers` | `AdminApi` | `bool/-` | `false` | `-` | `Public` | `Effective` |
| `CRANK_BOOTSTRAP_ADMIN_EMAIL` | `admin.bootstrap.email` | `AdminApi` | `string/-` | `required/blank` | `-` | `Internal` | `Effective` |
| `CRANK_BOOTSTRAP_ADMIN_PASSWORD` | `admin.bootstrap.password` | `AdminApi` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` |
| `CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME` | `admin.bootstrap.display_name` | `AdminApi` | `string/-` | `Crank Owner` | `-` | `Internal` | `Effective` |
| `CRANK_DEMO_SEED` | `admin.demo_seed` | `AdminApi` | `bool/-` | `false` | `-` | `Public` | `Effective` |
| `CRANK_MCP_BIND` | `mcp.bind` | `McpServer` | `socket/-` | `0.0.0.0:3002` | `-` | `Internal` | `Effective` |
| `CRANK_MCP_METRICS_BIND` | `mcp.metrics_bind` | `McpServer` | `socket/-` | `127.0.0.1:9465` | `-` | `Internal` | `Effective` |
| `CRANK_MCP_REFRESH_MS` | `mcp.refresh_ms` | `McpServer` | `u64/milliseconds` | `5000` | `100..=3600000` | `Public` | `Effective` |
| `CRANK_MCP_RATE_LIMIT_RPS` | `mcp.rate_limit.rps` | `McpServer` | `u32/requests_per_second` | `60` | `1..=100000` | `Public` | `Effective` |
| `CRANK_MCP_RATE_LIMIT_BURST` | `mcp.rate_limit.burst` | `McpServer` | `u32/requests` | `120` | `1..=1000000` | `Public` | `Effective` |
| `CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS` | `runtime.max_concurrent_sessions` | `McpServer` | `u32/sessions` | `16` | `1..=65535` | `Public` | `Effective` |
<!-- END GENERATED CRANK RUNTIME CONFIG -->
## PostgreSQL
Обязательные параметры:
@@ -22,6 +86,13 @@ Crank настраивается через переменные окружен
Если используется PgBouncer, укажите его адрес в `POSTGRES_HOST` и порт в `POSTGRES_PORT`.
`CRANK_DATABASE_URL` — compatibility-форма для существующих установок. Она
содержит credentials и поэтому никогда не выводится в diagnostics или
fingerprint. URL нельзя смешивать с явно заданными `POSTGRES_HOST`,
`POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER` или `POSTGRES_PASSWORD`:
конфликт останавливает startup. Для новых установок canonical-формой остаются
раздельные `POSTGRES_*` параметры.
## HTTP-сервисы
- `CRANK_ADMIN_BIND` - адрес `admin-api`, например `0.0.0.0:3001`.
@@ -79,11 +150,9 @@ Demo seed идемпотентный: повторный старт не соз
## Runtime limits
- `CRANK_RUNTIME_MAX_CONCURRENT_UNARY`
- `CRANK_RUNTIME_MAX_CONCURRENT_WINDOW`
- `CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS`
- `CRANK_RUNTIME_MAX_CONCURRENT_JOBS`
Эти настройки ограничивают параллельное выполнение операций и служебных задач.
Эти настройки ограничивают параллельное выполнение unary-запросов и MCP-сессий.
## Исходящие HTTP-запросы
@@ -124,9 +193,12 @@ CRANK_CACHE_BACKEND=memory
```env
CRANK_CACHE_BACKEND=valkey
CRANK_CACHE_URL=redis://valkey:6379/0
CRANK_CACHE_DEFAULT_TTL_MS=60000
```
`CRANK_CACHE_DEFAULT_TTL_MS` в прежних шаблонах не имел runtime consumer.
Непустое значение теперь отклоняется как deprecated no-effect configuration;
TTL задаётся владельцем конкретного cache operation.
Внешний кэш используется для служебного краткоживущего состояния: rate limiting, replay guard и опубликованные каталоги MCP-инструментов.
## Логи
@@ -141,6 +213,11 @@ CRANK_CACHE_DEFAULT_TTL_MS=60000
Поля с паролями, токенами, ключами, cookie, authorization, query, полным телом
или результатом очищаются до сериализации.
`crank-migrate` использует только database-поля этого контракта. Ему не
требуются и не должны передаваться master key, session/bootstrap secrets,
runtime, metrics или OTLP configuration. Команды и recovery contract описаны
в [migrations.md](migrations.md).
Пример:
```env
@@ -0,0 +1,143 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://crank.local/schemas/capability-baseline.schema.json",
"title": "Crank Community Capability Baseline Manifest",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"baseline_version",
"artifacts"
],
"properties": {
"schema_version": {
"const": 1
},
"baseline_version": {
"type": "string",
"pattern": "^[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}\\.[1-9][0-9]*$"
},
"artifacts": {
"type": "array",
"minItems": 5,
"maxItems": 5,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/artifact"
}
}
},
"$defs": {
"artifact": {
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"path",
"sha256"
],
"properties": {
"kind": {
"enum": [
"inventory",
"required_surfaces",
"taxonomy",
"checklist",
"results"
]
},
"path": {
"type": "string",
"minLength": 1,
"maxLength": 1024,
"pattern": "^(?!/)(?!.*//)(?!.*(?:^|/)\\.\\.?($|/))(?!.*\\\\).+$"
},
"sha256": {
"type": "string",
"pattern": "^[0-9a-f]{64}$"
}
}
},
"taxonomy": {
"type": "object",
"additionalProperties": false,
"required": ["baseline_version", "implementation_statuses", "execution_verdicts", "evidence_modes", "full_pass"],
"properties": {
"baseline_version": {"type": "string", "pattern": "^[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}\\.[1-9][0-9]*$"},
"implementation_statuses": {"type": "array", "minItems": 4, "maxItems": 4, "uniqueItems": true, "items": {"enum": ["implemented", "planned", "gap", "blocked"]}},
"execution_verdicts": {"type": "array", "minItems": 6, "maxItems": 6, "uniqueItems": true, "items": {"enum": ["pass", "fail", "blocked", "skipped", "flaky", "not_run"]}},
"evidence_modes": {"type": "array", "minItems": 2, "maxItems": 2, "uniqueItems": true, "items": {"enum": ["automated", "manual_only"]}},
"manual_only_rule": {"type": "string", "minLength": 1, "maxLength": 2048},
"non_pass_rule": {"type": "string", "minLength": 1, "maxLength": 2048},
"full_pass": {
"type": "object",
"additionalProperties": false,
"required": ["implementation_status", "execution_verdict", "evidence_mode"],
"properties": {
"implementation_status": {"const": "implemented"},
"execution_verdict": {"const": "pass"},
"evidence_mode": {"const": "automated"}
}
}
}
},
"run": {
"type": "object",
"additionalProperties": false,
"required": ["id", "command_id", "flow_ids", "execution_verdict", "evidence_mode", "accepted", "source_report_sha256", "source_revision", "environment_class", "collector"],
"properties": {
"id": {"type": "string", "minLength": 1, "maxLength": 128},
"command_id": {"type": "string", "minLength": 1, "maxLength": 128},
"flow_ids": {"type": "array", "minItems": 1, "maxItems": 1000, "uniqueItems": true, "items": {"type": "string", "minLength": 1, "maxLength": 128}},
"execution_verdict": {"enum": ["pass", "fail", "blocked", "skipped", "flaky", "not_run"]},
"evidence_mode": {"enum": ["automated", "manual_only"]},
"accepted": {"type": "boolean"},
"source_report_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
"source_revision": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"environment_class": {"type": "string", "minLength": 1, "maxLength": 64},
"collector": {"const": "capability-baseline-collector-v1"},
"summary": {"type": "object", "maxProperties": 16},
"safe_outcome": {"type": "object", "maxProperties": 16}
}
},
"defect": {
"type": "object",
"additionalProperties": false,
"required": ["id", "severity", "steps", "contract", "owner", "flow_ids", "next_action"],
"properties": {
"id": {"type": "string", "minLength": 1, "maxLength": 128},
"severity": {"enum": ["Critical", "High", "Medium", "Low"]},
"steps": {"type": "array", "minItems": 1, "maxItems": 20, "items": {"type": "string", "minLength": 1, "maxLength": 1024}},
"contract": {"type": "string", "minLength": 1, "maxLength": 2048},
"owner": {"type": "string", "minLength": 1, "maxLength": 128},
"flow_ids": {"type": "array", "minItems": 1, "maxItems": 1000, "uniqueItems": true, "items": {"type": "string", "minLength": 1, "maxLength": 128}},
"next_action": {"type": "string", "minLength": 1, "maxLength": 2048}
}
},
"manual_result": {
"type": "object",
"additionalProperties": false,
"required": ["check_id", "evidence_mode", "execution_verdict", "flow_ids", "next_evidence"],
"properties": {
"check_id": {"type": "string", "pattern": "^UI-[0-9]{2}$"},
"evidence_mode": {"const": "manual_only"},
"execution_verdict": {"enum": ["pass", "fail", "blocked", "skipped", "flaky", "not_run"]},
"flow_ids": {"type": "array", "minItems": 1, "maxItems": 1000, "uniqueItems": true, "items": {"type": "string", "minLength": 1, "maxLength": 128}},
"next_evidence": {"type": "string", "minLength": 1, "maxLength": 2048}
}
},
"results": {
"type": "object",
"additionalProperties": false,
"required": ["baseline_version", "source_revision", "environment_class", "runs", "manual_results", "defects"],
"properties": {
"baseline_version": {"type": "string", "maxLength": 32},
"source_revision": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"environment_class": {"type": "string", "minLength": 1, "maxLength": 64},
"runs": {"type": "array", "maxItems": 1000, "items": {"$ref": "#/$defs/run"}},
"manual_results": {"type": "array", "maxItems": 1000, "items": {"$ref": "#/$defs/manual_result"}},
"defects": {"type": "array", "maxItems": 1000, "items": {"$ref": "#/$defs/defect"}}
}
}
}
}
@@ -0,0 +1,141 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://crank.local/schemas/capability-inventory.schema.json",
"title": "Crank Community Capability Inventory",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"product",
"flows"
],
"properties": {
"schema_version": {
"const": 1
},
"product": {
"const": "crank-community"
},
"flows": {
"type": "array",
"minItems": 1,
"maxItems": 10000,
"items": {
"$ref": "#/$defs/flow"
}
}
},
"$defs": {
"flow": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"type",
"requirements",
"user_outcome",
"owner",
"status",
"capabilities",
"evidence"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
},
"type": {
"enum": [
"ui",
"api",
"mcp"
]
},
"requirements": {
"type": "array",
"minItems": 1,
"maxItems": 64,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^FR-[1-9][0-9]*$"
}
},
"user_outcome": {
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "[\\s\\S]*\\S[\\s\\S]*"
},
"owner": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "[\\s\\S]*\\S[\\s\\S]*"
},
"status": {
"enum": [
"implemented",
"planned",
"gap",
"blocked"
]
},
"capabilities": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"enum": [
"tools",
"resources",
"prompts",
"tasks",
"load_runs"
]
}
},
"evidence": {
"$ref": "#/$defs/evidence"
},
"notes": {
"type": "string",
"maxLength": 4096
}
}
},
"evidence": {
"type": "object",
"additionalProperties": false,
"required": [
"automated",
"manual"
],
"properties": {
"automated": {
"$ref": "#/$defs/evidencePaths"
},
"manual": {
"$ref": "#/$defs/evidencePaths"
}
}
},
"evidencePaths": {
"type": "array",
"minItems": 1,
"maxItems": 64,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 1024,
"pattern": "^(?!/)(?!.*//)(?!.*(?:^|/)\\.\\.?($|/))(?!.*\\\\).+$"
}
}
}
}
+1
View File
@@ -0,0 +1 @@
{"schema_version":1,"sequence":[{"backfill":{"kind":"none"},"checksum":"crank-community-baseline-v1","compatibility":"legacy-baseline","contract_evidence":null,"name":"community-baseline-v1","owner":"crank-registry","phase":"expand","readable_schema_max":1,"readable_schema_min":1,"source_digest":"eb1656fc5b4b5be9ee390d237d1d58e4b2274ae5ba9b7ba06a2f3f860dfda675","transactional":true,"version":1},{"backfill":{"kind":"none"},"checksum":"1908b97ca7fe8a85d146b0eebf007d5018a9ff406cb099614270f3428ec1db48","compatibility":"n-minus-one-readable","contract_evidence":null,"name":"legacy-consolidation-v2","owner":"crank-registry","phase":"expand","readable_schema_max":2,"readable_schema_min":1,"source_digest":"1908b97ca7fe8a85d146b0eebf007d5018a9ff406cb099614270f3428ec1db48","transactional":true,"version":2},{"backfill":{"kind":"none"},"checksum":"36487625503a8d4d8f18d5771c3c9b6705f845e267acbfcb7244c330f640cd94","compatibility":"n-minus-one-readable","contract_evidence":null,"name":"request-trace-identity-v3","owner":"crank-registry","phase":"expand","readable_schema_max":3,"readable_schema_min":2,"source_digest":"36487625503a8d4d8f18d5771c3c9b6705f845e267acbfcb7244c330f640cd94","transactional":true,"version":3}]}
+898
View File
@@ -0,0 +1,898 @@
{
"schema_version": 1,
"generated_by": "crank-config",
"fields": [
{
"semantic_path": "database.url",
"env_name": "CRANK_DATABASE_URL",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": "legacy URL form",
"rules": [
"takes precedence over generated default-valued POSTGRES_HOST/PORT/DB/USER/PASSWORD",
"conflicts with any non-default decomposed database value"
]
},
{
"semantic_path": "database.host",
"env_name": "POSTGRES_HOST",
"process": "shared",
"value_type": "string",
"unit": null,
"default": "postgres",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.port",
"env_name": "POSTGRES_PORT",
"process": "shared",
"value_type": "u16",
"unit": "port",
"default": "5432",
"required": false,
"minimum": 1,
"maximum": 65535,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.name",
"env_name": "POSTGRES_DB",
"process": "shared",
"value_type": "string",
"unit": null,
"default": "crank",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.user",
"env_name": "POSTGRES_USER",
"process": "shared",
"value_type": "string",
"unit": null,
"default": "crank",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.password",
"env_name": "POSTGRES_PASSWORD",
"process": "shared",
"value_type": "secret",
"unit": null,
"default": "configured",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.pool.max_connections",
"env_name": "POSTGRES_MAX_CONNECTIONS",
"process": "shared",
"value_type": "u32",
"unit": "connections",
"default": "20",
"required": false,
"minimum": 1,
"maximum": 1024,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": [
"must be >= min_connections"
]
},
{
"semantic_path": "database.pool.min_connections",
"env_name": "POSTGRES_MIN_CONNECTIONS",
"process": "shared",
"value_type": "u32",
"unit": "connections",
"default": "2",
"required": false,
"minimum": 0,
"maximum": 1024,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": [
"must be <= max_connections"
]
},
{
"semantic_path": "database.pool.acquire_timeout_ms",
"env_name": "POSTGRES_ACQUIRE_TIMEOUT_MS",
"process": "shared",
"value_type": "u64",
"unit": "milliseconds",
"default": "5000",
"required": false,
"minimum": 1,
"maximum": 300000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.pool.idle_timeout_ms",
"env_name": "POSTGRES_IDLE_TIMEOUT_MS",
"process": "shared",
"value_type": "u64",
"unit": "milliseconds",
"default": "600000",
"required": false,
"minimum": 1000,
"maximum": 86400000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "database.pool.max_lifetime_ms",
"env_name": "POSTGRES_MAX_LIFETIME_MS",
"process": "shared",
"value_type": "u64",
"unit": "milliseconds",
"default": "1800000",
"required": false,
"minimum": 1000,
"maximum": 86400000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "runtime.master_key",
"env_name": "CRANK_MASTER_KEY",
"process": "shared",
"value_type": "secret",
"unit": null,
"default": null,
"required": true,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "runtime.base_url",
"env_name": "CRANK_BASE_URL",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "runtime.max_concurrent_unary",
"env_name": "CRANK_RUNTIME_MAX_CONCURRENT_UNARY",
"process": "shared",
"value_type": "u32",
"unit": "requests",
"default": "64",
"required": false,
"minimum": 1,
"maximum": 65535,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "cache.backend",
"env_name": "CRANK_CACHE_BACKEND",
"process": "shared",
"value_type": "enum",
"unit": null,
"default": "memory",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": "redis value is deprecated in favor of valkey",
"rules": [
"external backend requires cache.url"
]
},
{
"semantic_path": "cache.url",
"env_name": "CRANK_CACHE_URL",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": [
"forbidden with memory backend"
]
},
{
"semantic_path": "cache.default_ttl_ms",
"env_name": "CRANK_CACHE_DEFAULT_TTL_MS",
"process": "shared",
"value_type": "u64",
"unit": "milliseconds",
"default": null,
"required": false,
"minimum": 1,
"maximum": 86400000,
"sensitivity": "public",
"mode": "deprecated_no_effect",
"compatibility": null,
"rules": []
},
{
"semantic_path": "outbound.allowed_hosts",
"env_name": "CRANK_OUTBOUND_ALLOWED_HOSTS",
"process": "shared",
"value_type": "host_list",
"unit": null,
"default": "",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "outbound.denied_hosts",
"env_name": "CRANK_OUTBOUND_DENIED_HOSTS",
"process": "shared",
"value_type": "host_list",
"unit": null,
"default": "",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": [
"deny entries override allow entries"
]
},
{
"semantic_path": "outbound.max_response_bytes",
"env_name": "CRANK_OUTBOUND_MAX_RESPONSE_BYTES",
"process": "shared",
"value_type": "u64",
"unit": "bytes",
"default": "4194304",
"required": false,
"minimum": 1,
"maximum": 67108864,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.environment",
"env_name": "CRANK_ENVIRONMENT",
"process": "shared",
"value_type": "label",
"unit": null,
"default": "development",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.log_filter",
"env_name": "CRANK_LOG_LEVEL",
"process": "shared",
"value_type": "string",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.sentry_dsn",
"env_name": "CRANK_SENTRY_DSN",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.metrics.enabled",
"env_name": "CRANK_METRICS_ENABLED",
"process": "shared",
"value_type": "bool",
"unit": null,
"default": "true",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": "yes/no/on/off spellings are deprecated",
"rules": []
},
{
"semantic_path": "observability.metrics.bearer_token",
"env_name": "CRANK_METRICS_BEARER_TOKEN",
"process": "shared",
"value_type": "secret",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": [
"required when an enabled metrics bind is non-loopback"
]
},
{
"semantic_path": "observability.otlp.endpoint",
"env_name": "OTEL_EXPORTER_OTLP_ENDPOINT",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.traces_endpoint",
"env_name": "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
"process": "shared",
"value_type": "url",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": [
"overrides generic OTLP endpoint"
]
},
{
"semantic_path": "observability.otlp.protocol",
"env_name": "OTEL_EXPORTER_OTLP_PROTOCOL",
"process": "shared",
"value_type": "enum",
"unit": null,
"default": "http/protobuf",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.traces_protocol",
"env_name": "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL",
"process": "shared",
"value_type": "enum",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.timeout",
"env_name": "OTEL_EXPORTER_OTLP_TIMEOUT",
"process": "shared",
"value_type": "duration",
"unit": "milliseconds",
"default": "10000",
"required": false,
"minimum": 1,
"maximum": 300000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.traces_timeout",
"env_name": "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT",
"process": "shared",
"value_type": "duration",
"unit": "milliseconds",
"default": null,
"required": false,
"minimum": 1,
"maximum": 300000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.headers",
"env_name": "OTEL_EXPORTER_OTLP_HEADERS",
"process": "shared",
"value_type": "headers",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.traces_headers",
"env_name": "OTEL_EXPORTER_OTLP_TRACES_HEADERS",
"process": "shared",
"value_type": "headers",
"unit": null,
"default": null,
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.max_queue_size",
"env_name": "OTEL_BSP_MAX_QUEUE_SIZE",
"process": "shared",
"value_type": "u32",
"unit": "spans",
"default": "2048",
"required": false,
"minimum": 1,
"maximum": 65536,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.max_export_batch_size",
"env_name": "OTEL_BSP_MAX_EXPORT_BATCH_SIZE",
"process": "shared",
"value_type": "u32",
"unit": "spans",
"default": "512",
"required": false,
"minimum": 1,
"maximum": 65536,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": [
"must be <= max_queue_size"
]
},
{
"semantic_path": "observability.otlp.schedule_delay",
"env_name": "OTEL_BSP_SCHEDULE_DELAY",
"process": "shared",
"value_type": "duration",
"unit": "milliseconds",
"default": "5000",
"required": false,
"minimum": 1,
"maximum": 300000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "observability.otlp.export_timeout",
"env_name": "OTEL_BSP_EXPORT_TIMEOUT",
"process": "shared",
"value_type": "duration",
"unit": "milliseconds",
"default": "30000",
"required": false,
"minimum": 1,
"maximum": 300000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.bind",
"env_name": "CRANK_ADMIN_BIND",
"process": "admin_api",
"value_type": "socket",
"unit": null,
"default": "0.0.0.0:3001",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.metrics_bind",
"env_name": "CRANK_ADMIN_METRICS_BIND",
"process": "admin_api",
"value_type": "socket",
"unit": null,
"default": "127.0.0.1:9464",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.storage_root",
"env_name": "CRANK_STORAGE_ROOT",
"process": "admin_api",
"value_type": "absolute_path",
"unit": null,
"default": "/var/lib/crank/storage",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.rate_limit.rps",
"env_name": "CRANK_ADMIN_RATE_LIMIT_RPS",
"process": "admin_api",
"value_type": "u32",
"unit": "requests_per_second",
"default": "30",
"required": false,
"minimum": 1,
"maximum": 100000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.rate_limit.burst",
"env_name": "CRANK_ADMIN_RATE_LIMIT_BURST",
"process": "admin_api",
"value_type": "u32",
"unit": "requests",
"default": "60",
"required": false,
"minimum": 1,
"maximum": 1000000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": [
"must be >= admin rate RPS"
]
},
{
"semantic_path": "admin.invocation_log_retention_days",
"env_name": "CRANK_INVOCATION_LOG_RETENTION_DAYS",
"process": "admin_api",
"value_type": "u32",
"unit": "days",
"default": "30",
"required": false,
"minimum": 1,
"maximum": 36500,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.session.secret",
"env_name": "CRANK_SESSION_SECRET",
"process": "admin_api",
"value_type": "secret",
"unit": null,
"default": null,
"required": true,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.password_pepper",
"env_name": "CRANK_PASSWORD_PEPPER",
"process": "admin_api",
"value_type": "secret",
"unit": null,
"default": null,
"required": true,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.session.ttl_hours",
"env_name": "CRANK_SESSION_TTL_HOURS",
"process": "admin_api",
"value_type": "u32",
"unit": "hours",
"default": "24",
"required": false,
"minimum": 1,
"maximum": 8760,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.trust_forwarded_headers",
"env_name": "CRANK_TRUST_FORWARDED_HEADERS",
"process": "admin_api",
"value_type": "bool",
"unit": null,
"default": "false",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": "yes/no/on/off spellings are deprecated",
"rules": []
},
{
"semantic_path": "admin.bootstrap.email",
"env_name": "CRANK_BOOTSTRAP_ADMIN_EMAIL",
"process": "admin_api",
"value_type": "string",
"unit": null,
"default": null,
"required": true,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.bootstrap.password",
"env_name": "CRANK_BOOTSTRAP_ADMIN_PASSWORD",
"process": "admin_api",
"value_type": "secret",
"unit": null,
"default": null,
"required": true,
"minimum": null,
"maximum": null,
"sensitivity": "secret",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.bootstrap.display_name",
"env_name": "CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME",
"process": "admin_api",
"value_type": "string",
"unit": null,
"default": "Crank Owner",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "admin.demo_seed",
"env_name": "CRANK_DEMO_SEED",
"process": "admin_api",
"value_type": "bool",
"unit": null,
"default": "false",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "public",
"mode": "effective",
"compatibility": "yes/no/on/off spellings are deprecated",
"rules": []
},
{
"semantic_path": "mcp.bind",
"env_name": "CRANK_MCP_BIND",
"process": "mcp_server",
"value_type": "socket",
"unit": null,
"default": "0.0.0.0:3002",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "mcp.metrics_bind",
"env_name": "CRANK_MCP_METRICS_BIND",
"process": "mcp_server",
"value_type": "socket",
"unit": null,
"default": "127.0.0.1:9465",
"required": false,
"minimum": null,
"maximum": null,
"sensitivity": "internal",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "mcp.refresh_ms",
"env_name": "CRANK_MCP_REFRESH_MS",
"process": "mcp_server",
"value_type": "u64",
"unit": "milliseconds",
"default": "5000",
"required": false,
"minimum": 100,
"maximum": 3600000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "mcp.rate_limit.rps",
"env_name": "CRANK_MCP_RATE_LIMIT_RPS",
"process": "mcp_server",
"value_type": "u32",
"unit": "requests_per_second",
"default": "60",
"required": false,
"minimum": 1,
"maximum": 100000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
},
{
"semantic_path": "mcp.rate_limit.burst",
"env_name": "CRANK_MCP_RATE_LIMIT_BURST",
"process": "mcp_server",
"value_type": "u32",
"unit": "requests",
"default": "120",
"required": false,
"minimum": 1,
"maximum": 1000000,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": [
"must be >= MCP rate RPS"
]
},
{
"semantic_path": "runtime.max_concurrent_sessions",
"env_name": "CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS",
"process": "mcp_server",
"value_type": "u32",
"unit": "sessions",
"default": "16",
"required": false,
"minimum": 1,
"maximum": 65535,
"sensitivity": "public",
"mode": "effective",
"compatibility": null,
"rules": []
}
],
"deployment_only_fields": [
"COMPOSE_PROJECT_NAME",
"POSTGRES_PUBLISH_BIND",
"POSTGRES_PUBLISH_PORT",
"CRANK_ADMIN_API_IMAGE",
"CRANK_MCP_SERVER_IMAGE",
"CRANK_UI_IMAGE",
"CRANK_PUBLISH_BIND",
"CRANK_ADMIN_PUBLISH_PORT",
"CRANK_MCP_PUBLISH_PORT",
"CRANK_UI_PUBLISH_PORT",
"VALKEY_PUBLISH_BIND",
"VALKEY_PUBLISH_PORT"
]
}