create-flowdular 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/agent-template/.agents/skills/agent-tool-design/SKILL.md +203 -0
- package/agent-template/.agents/skills/auth-security-review/SKILL.md +90 -0
- package/agent-template/.agents/skills/auto-review/SKILL.md +103 -0
- package/agent-template/.agents/skills/bug-hunt/SKILL.md +104 -0
- package/agent-template/.agents/skills/business-agent-design/SKILL.md +182 -0
- package/agent-template/.agents/skills/cli-extension/SKILL.md +108 -0
- package/agent-template/.agents/skills/core-extend/SKILL.md +99 -0
- package/agent-template/.agents/skills/database-adapter/SKILL.md +198 -0
- package/agent-template/.agents/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.agents/skills/migration-authoring/SKILL.md +161 -0
- package/agent-template/.agents/skills/module-new/SKILL.md +171 -0
- package/agent-template/.agents/skills/module-update/SKILL.md +91 -0
- package/agent-template/.agents/skills/perf-audit/SKILL.md +98 -0
- package/agent-template/.agents/skills/release-eject-pr/SKILL.md +107 -0
- package/agent-template/.agents/skills/spec-approval/SKILL.md +106 -0
- package/agent-template/.agents/skills/test-hardening/SKILL.md +79 -0
- package/agent-template/.agents/skills/translations-i18n/SKILL.md +78 -0
- package/agent-template/.agents/skills/ux-design/SKILL.md +92 -0
- package/agent-template/.agents/skills/variables/SKILL.md +156 -0
- package/agent-template/.agents/skills/workflow-development/SKILL.md +192 -0
- package/agent-template/.ai/README.md +62 -0
- package/agent-template/.ai/agents/README.md +27 -0
- package/agent-template/.ai/agents/module-executor.md +36 -0
- package/agent-template/.ai/agents/reviewer.md +23 -0
- package/agent-template/.ai/agents/sandbox/agentic-engineer.md +31 -0
- package/agent-template/.ai/agents/sandbox/backend-engineer.md +36 -0
- package/agent-template/.ai/agents/sandbox/business-manager.md +23 -0
- package/agent-template/.ai/agents/sandbox/frontend-engineer.md +27 -0
- package/agent-template/.ai/agents/sandbox/ux-designer.md +23 -0
- package/agent-template/.ai/agents/spec-author.md +29 -0
- package/agent-template/.ai/blueprints/add-migration/README.md +5 -0
- package/agent-template/.ai/blueprints/add-migration/allowed-paths.yaml +23 -0
- package/agent-template/.ai/blueprints/add-migration/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/add-migration/examples/invalid/input-destructive.json +6 -0
- package/agent-template/.ai/blueprints/add-migration/examples/invalid/plan-unnumbered-file.json +9 -0
- package/agent-template/.ai/blueprints/add-migration/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/add-migration/examples/valid/plan.json +9 -0
- package/agent-template/.ai/blueprints/add-migration/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/add-migration/input.schema.json +23 -0
- package/agent-template/.ai/blueprints/add-migration/plan.schema.json +54 -0
- package/agent-template/.ai/blueprints/add-migration/required-files.yaml +18 -0
- package/agent-template/.ai/blueprints/add-migration/spec-requirements.yaml +13 -0
- package/agent-template/.ai/blueprints/add-migration/steps.yaml +62 -0
- package/agent-template/.ai/blueprints/author-spec/README.md +5 -0
- package/agent-template/.ai/blueprints/author-spec/allowed-paths.yaml +7 -0
- package/agent-template/.ai/blueprints/author-spec/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/author-spec/examples/invalid/input-missing-outcome.json +5 -0
- package/agent-template/.ai/blueprints/author-spec/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/author-spec/gates.yaml +13 -0
- package/agent-template/.ai/blueprints/author-spec/input.schema.json +20 -0
- package/agent-template/.ai/blueprints/author-spec/plan.schema.json +14 -0
- package/agent-template/.ai/blueprints/author-spec/required-files.yaml +6 -0
- package/agent-template/.ai/blueprints/author-spec/spec-requirements.yaml +35 -0
- package/agent-template/.ai/blueprints/author-spec/steps.yaml +28 -0
- package/agent-template/.ai/blueprints/author-spec/templates/module.yaml +45 -0
- package/agent-template/.ai/blueprints/bug-fix/README.md +5 -0
- package/agent-template/.ai/blueprints/bug-fix/allowed-paths.yaml +29 -0
- package/agent-template/.ai/blueprints/bug-fix/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/invalid/input-no-symptom.json +4 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/invalid/plan-no-test.json +8 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/valid/plan.json +8 -0
- package/agent-template/.ai/blueprints/bug-fix/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/bug-fix/input.schema.json +25 -0
- package/agent-template/.ai/blueprints/bug-fix/plan.schema.json +53 -0
- package/agent-template/.ai/blueprints/bug-fix/required-files.yaml +7 -0
- package/agent-template/.ai/blueprints/bug-fix/spec-requirements.yaml +7 -0
- package/agent-template/.ai/blueprints/bug-fix/steps.yaml +51 -0
- package/agent-template/.ai/blueprints/core-extend/README.md +5 -0
- package/agent-template/.ai/blueprints/core-extend/allowed-paths.yaml +49 -0
- package/agent-template/.ai/blueprints/core-extend/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/core-extend/examples/invalid/input-unknown-package.json +5 -0
- package/agent-template/.ai/blueprints/core-extend/examples/invalid/plan-missing-gates.json +7 -0
- package/agent-template/.ai/blueprints/core-extend/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/core-extend/examples/valid/plan.json +10 -0
- package/agent-template/.ai/blueprints/core-extend/gates.yaml +16 -0
- package/agent-template/.ai/blueprints/core-extend/input.schema.json +54 -0
- package/agent-template/.ai/blueprints/core-extend/plan.schema.json +39 -0
- package/agent-template/.ai/blueprints/core-extend/required-files.yaml +36 -0
- package/agent-template/.ai/blueprints/core-extend/spec-requirements.yaml +17 -0
- package/agent-template/.ai/blueprints/core-extend/steps.yaml +54 -0
- package/agent-template/.ai/blueprints/edit-module/README.md +9 -0
- package/agent-template/.ai/blueprints/edit-module/allowed-paths.yaml +27 -0
- package/agent-template/.ai/blueprints/edit-module/blueprint.json +20 -0
- package/agent-template/.ai/blueprints/edit-module/examples/invalid/input-unknown-change.json +5 -0
- package/agent-template/.ai/blueprints/edit-module/examples/invalid/plan-touches-platform.json +15 -0
- package/agent-template/.ai/blueprints/edit-module/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/edit-module/examples/valid/plan.json +25 -0
- package/agent-template/.ai/blueprints/edit-module/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/edit-module/input.schema.json +31 -0
- package/agent-template/.ai/blueprints/edit-module/plan.schema.json +65 -0
- package/agent-template/.ai/blueprints/edit-module/required-files.yaml +80 -0
- package/agent-template/.ai/blueprints/edit-module/spec-requirements.yaml +15 -0
- package/agent-template/.ai/blueprints/edit-module/steps.yaml +115 -0
- package/agent-template/.ai/blueprints/new-module/README.md +7 -0
- package/agent-template/.ai/blueprints/new-module/allowed-paths.yaml +27 -0
- package/agent-template/.ai/blueprints/new-module/blueprint.json +20 -0
- package/agent-template/.ai/blueprints/new-module/examples/invalid/input-spec-outside-modules.json +4 -0
- package/agent-template/.ai/blueprints/new-module/examples/invalid/plan-unknown-gate.json +8 -0
- package/agent-template/.ai/blueprints/new-module/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/new-module/examples/valid/plan.json +22 -0
- package/agent-template/.ai/blueprints/new-module/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/new-module/input.schema.json +21 -0
- package/agent-template/.ai/blueprints/new-module/plan.schema.json +58 -0
- package/agent-template/.ai/blueprints/new-module/required-files.yaml +73 -0
- package/agent-template/.ai/blueprints/new-module/spec-requirements.yaml +30 -0
- package/agent-template/.ai/blueprints/new-module/steps.yaml +138 -0
- package/agent-template/.ai/blueprints/release/README.md +5 -0
- package/agent-template/.ai/blueprints/release/allowed-paths.yaml +19 -0
- package/agent-template/.ai/blueprints/release/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/release/examples/invalid/input-bad-version.json +4 -0
- package/agent-template/.ai/blueprints/release/examples/invalid/plan-bad-branch.json +7 -0
- package/agent-template/.ai/blueprints/release/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/release/examples/valid/plan.json +20 -0
- package/agent-template/.ai/blueprints/release/gates.yaml +20 -0
- package/agent-template/.ai/blueprints/release/input.schema.json +24 -0
- package/agent-template/.ai/blueprints/release/plan.schema.json +46 -0
- package/agent-template/.ai/blueprints/release/required-files.yaml +19 -0
- package/agent-template/.ai/blueprints/release/spec-requirements.yaml +8 -0
- package/agent-template/.ai/blueprints/release/steps.yaml +47 -0
- package/agent-template/.ai/blueprints/security-review/README.md +5 -0
- package/agent-template/.ai/blueprints/security-review/allowed-paths.yaml +6 -0
- package/agent-template/.ai/blueprints/security-review/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/security-review/examples/invalid/input-unknown-kind.json +4 -0
- package/agent-template/.ai/blueprints/security-review/examples/invalid/plan-finding-without-scenario.json +14 -0
- package/agent-template/.ai/blueprints/security-review/examples/valid/input.json +4 -0
- package/agent-template/.ai/blueprints/security-review/examples/valid/plan.json +19 -0
- package/agent-template/.ai/blueprints/security-review/gates.yaml +22 -0
- package/agent-template/.ai/blueprints/security-review/input.schema.json +20 -0
- package/agent-template/.ai/blueprints/security-review/plan.schema.json +65 -0
- package/agent-template/.ai/blueprints/security-review/required-files.yaml +6 -0
- package/agent-template/.ai/blueprints/security-review/spec-requirements.yaml +9 -0
- package/agent-template/.ai/blueprints/security-review/steps.yaml +38 -0
- package/agent-template/.ai/examples/README.md +8 -0
- package/agent-template/.ai/examples/bad/client-imports-server/README.md +20 -0
- package/agent-template/.ai/examples/bad/client-imports-server/api.ts +12 -0
- package/agent-template/.ai/examples/bad/missing-acl/README.md +23 -0
- package/agent-template/.ai/examples/bad/missing-acl/endpoints.ts +12 -0
- package/agent-template/.ai/examples/bad/tenant-from-body/README.md +19 -0
- package/agent-template/.ai/examples/bad/tenant-from-body/endpoints.ts +33 -0
- package/agent-template/.ai/examples/client-contribution/CustomerListView.tsrx +34 -0
- package/agent-template/.ai/examples/client-contribution/README.md +11 -0
- package/agent-template/.ai/examples/client-contribution/contribution.tsrx +48 -0
- package/agent-template/.ai/examples/client-contribution/index.ts +20 -0
- package/agent-template/.ai/examples/client-contribution/permissions.ts +8 -0
- package/agent-template/.ai/examples/customer-cli-extension/README.md +14 -0
- package/agent-template/.ai/examples/customer-cli-extension/commands.json +17 -0
- package/agent-template/.ai/examples/customer-cli-extension/index.ts +36 -0
- package/agent-template/.ai/examples/module-create/task-packet.json +11 -0
- package/agent-template/.ai/guides/application-development.md +97 -0
- package/agent-template/.ai/policies/capabilities.yaml +164 -0
- package/agent-template/.ai/policies/model-routing.yaml +72 -0
- package/agent-template/.ai/policies/path-ownership.yaml +65 -0
- package/agent-template/.ai/policies/task-budgets.yaml +37 -0
- package/agent-template/.ai/references/catalog/LICENSE +21 -0
- package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.down.sql +2 -0
- package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.up.sql +21 -0
- package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.up.sql +20 -0
- package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.up.sql +36 -0
- package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.up.sql +19 -0
- package/agent-template/.ai/references/catalog/migrations/README.md +3 -0
- package/agent-template/.ai/references/catalog/module.json +27 -0
- package/agent-template/.ai/references/catalog/package.json +49 -0
- package/agent-template/.ai/references/catalog/spec/module.yaml +86 -0
- package/agent-template/.ai/references/catalog/src/acl/permissions.ts +6 -0
- package/agent-template/.ai/references/catalog/src/agent/tools.ts +164 -0
- package/agent-template/.ai/references/catalog/src/api/endpoints.ts +243 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogHistoryDrawer.tsrx +123 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogItemForm.tsrx +190 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogView.tsrx +473 -0
- package/agent-template/.ai/references/catalog/src/client/api.ts +111 -0
- package/agent-template/.ai/references/catalog/src/client/contribution.tsrx +61 -0
- package/agent-template/.ai/references/catalog/src/client/index.ts +18 -0
- package/agent-template/.ai/references/catalog/src/client/navigation-copy.ts +9 -0
- package/agent-template/.ai/references/catalog/src/client/state.ts +24 -0
- package/agent-template/.ai/references/catalog/src/domain/types.ts +32 -0
- package/agent-template/.ai/references/catalog/src/domain/variables.ts +111 -0
- package/agent-template/.ai/references/catalog/src/index.ts +31 -0
- package/agent-template/.ai/references/catalog/src/platform.ts +35 -0
- package/agent-template/.ai/references/catalog/src/server/index.ts +4 -0
- package/agent-template/.ai/references/catalog/src/server/runtime.ts +86 -0
- package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +306 -0
- package/agent-template/.ai/references/catalog/src/services/database-repository.ts +440 -0
- package/agent-template/.ai/references/catalog/src/services/index.ts +4 -0
- package/agent-template/.ai/references/catalog/src/services/migration.ts +171 -0
- package/agent-template/.ai/references/catalog/src/services/repository.ts +36 -0
- package/agent-template/.ai/references/catalog/src/services/target-idempotency.ts +59 -0
- package/agent-template/.ai/references/catalog/tests/agent-tools.test.ts +277 -0
- package/agent-template/.ai/references/catalog/tests/endpoints.test.ts +320 -0
- package/agent-template/.ai/references/catalog/tests/idempotency.test.ts +297 -0
- package/agent-template/.ai/references/catalog/tests/migrations.test.ts +149 -0
- package/agent-template/.ai/references/catalog/tests/module.test.ts +271 -0
- package/agent-template/.ai/references/catalog/tests/support/database.ts +76 -0
- package/agent-template/.ai/references/catalog/translations/en.json +101 -0
- package/agent-template/.ai/references/catalog/translations/pl.json +101 -0
- package/agent-template/.ai/references/catalog/tsconfig.json +15 -0
- package/agent-template/.ai/references/catalog/vitest.config.ts +16 -0
- package/agent-template/.ai/references/catalog.provenance.json +55 -0
- package/agent-template/.ai/rules/flowdular.md +86 -0
- package/agent-template/.ai/skills/README.md +36 -0
- package/agent-template/.ai/skills/agent-tool-design/SKILL.md +209 -0
- package/agent-template/.ai/skills/auth-security-review/SKILL.md +96 -0
- package/agent-template/.ai/skills/auto-review/SKILL.md +112 -0
- package/agent-template/.ai/skills/bug-hunt/SKILL.md +110 -0
- package/agent-template/.ai/skills/business-agent-design/SKILL.md +188 -0
- package/agent-template/.ai/skills/cli-extension/SKILL.md +114 -0
- package/agent-template/.ai/skills/core-extend/SKILL.md +104 -0
- package/agent-template/.ai/skills/database-adapter/SKILL.md +204 -0
- package/agent-template/.ai/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.ai/skills/migration-authoring/SKILL.md +167 -0
- package/agent-template/.ai/skills/module-new/SKILL.md +180 -0
- package/agent-template/.ai/skills/module-update/SKILL.md +100 -0
- package/agent-template/.ai/skills/perf-audit/SKILL.md +105 -0
- package/agent-template/.ai/skills/release-eject-pr/SKILL.md +113 -0
- package/agent-template/.ai/skills/spec-approval/SKILL.md +112 -0
- package/agent-template/.ai/skills/test-hardening/SKILL.md +86 -0
- package/agent-template/.ai/skills/translations-i18n/SKILL.md +85 -0
- package/agent-template/.ai/skills/ux-design/SKILL.md +97 -0
- package/agent-template/.ai/skills/variables/SKILL.md +164 -0
- package/agent-template/.ai/skills/workflow-development/SKILL.md +199 -0
- package/agent-template/.claude/skills/agent-tool-design/SKILL.md +203 -0
- package/agent-template/.claude/skills/auth-security-review/SKILL.md +90 -0
- package/agent-template/.claude/skills/auto-review/SKILL.md +103 -0
- package/agent-template/.claude/skills/bug-hunt/SKILL.md +104 -0
- package/agent-template/.claude/skills/business-agent-design/SKILL.md +182 -0
- package/agent-template/.claude/skills/cli-extension/SKILL.md +108 -0
- package/agent-template/.claude/skills/core-extend/SKILL.md +99 -0
- package/agent-template/.claude/skills/database-adapter/SKILL.md +198 -0
- package/agent-template/.claude/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.claude/skills/migration-authoring/SKILL.md +161 -0
- package/agent-template/.claude/skills/module-new/SKILL.md +171 -0
- package/agent-template/.claude/skills/module-update/SKILL.md +91 -0
- package/agent-template/.claude/skills/perf-audit/SKILL.md +98 -0
- package/agent-template/.claude/skills/release-eject-pr/SKILL.md +107 -0
- package/agent-template/.claude/skills/spec-approval/SKILL.md +106 -0
- package/agent-template/.claude/skills/test-hardening/SKILL.md +79 -0
- package/agent-template/.claude/skills/translations-i18n/SKILL.md +78 -0
- package/agent-template/.claude/skills/ux-design/SKILL.md +92 -0
- package/agent-template/.claude/skills/variables/SKILL.md +156 -0
- package/agent-template/.claude/skills/workflow-development/SKILL.md +192 -0
- package/agent-template/AGENTS.md +77 -0
- package/agent-template/CLAUDE.md +77 -0
- package/agent-template/docs/adr/0001-development-reload.md +16 -0
- package/agent-template/docs/adr/0002-durable-agent-execution.md +21 -0
- package/agent-template/docs/adr/0003-module-settings.md +22 -0
- package/agent-template/docs/adr/0004-enterprise-access-and-audit.md +36 -0
- package/agent-template/docs/adr/0005-sandbox-runtime-and-coding-agents.md +81 -0
- package/agent-template/docs/adr/0006-agentic-workflows.md +1702 -0
- package/agent-template/docs/adr/0007-module-owned-agents.md +429 -0
- package/agent-template/docs/adr/0008-database-adapter-contract.md +90 -0
- package/agent-template/docs/agent-contract.md +45 -0
- package/agent-template/docs/configuration.md +122 -0
- package/agent-template/docs/database-adapters.md +346 -0
- package/agent-template/docs/design-system.md +217 -0
- package/agent-template/docs/modules.md +146 -0
- package/agent-template/platform/scripts/build.mjs +38 -0
- package/agent-template/rulesync.jsonc +11 -0
- package/dist/bin.js +40 -2
- package/package.json +3 -2
- package/template/default/.prettierignore +9 -0
- package/template/default/README.md +12 -0
- package/template/default/flowdular.json +3 -3
- package/template/default/modules/example/package.json +2 -2
- package/template/default/package.json +6 -2
- package/template/default/platform/octane.config.ts +17 -6
- package/template/default/platform/package.json +3 -2
- package/template/default/pnpm-workspace.yaml +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
write: []
|
|
3
|
+
deny:
|
|
4
|
+
- '**'
|
|
5
|
+
notes:
|
|
6
|
+
- a release commits what the CLI produced; nothing is edited by hand
|
|
7
|
+
# Written only by CLI commands, never by hand. The eject and the direct path
|
|
8
|
+
# both run the commands; the files still travel in the same commit.
|
|
9
|
+
cliOwned:
|
|
10
|
+
- path: flowdular.json
|
|
11
|
+
command: pnpm flowdular module enable <id> --apply
|
|
12
|
+
- path: platform/package.json
|
|
13
|
+
command: pnpm flowdular module enable <id> --apply
|
|
14
|
+
- path: platform/src/generated/modules.server.ts
|
|
15
|
+
command: pnpm flowdular module sync --apply
|
|
16
|
+
- path: platform/src/generated/modules.client.ts
|
|
17
|
+
command: pnpm flowdular module sync --apply
|
|
18
|
+
- path: pnpm-lock.yaml
|
|
19
|
+
command: pnpm install
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../packages/contracts/schemas/blueprint.schema.json",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"architecture": "0.2.0",
|
|
5
|
+
"owner": "platform-architecture",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"id": "release",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"risk": "process",
|
|
10
|
+
"agentRoles": ["module-executor"],
|
|
11
|
+
"executorProfiles": ["repo-agent"],
|
|
12
|
+
"requiredReviewers": ["reviewer", "platform-owner"],
|
|
13
|
+
"requiresApprovedSpec": false
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"blueprint": "release@1.0.0",
|
|
3
|
+
"branch": "sandbox/inventory-3f1c2d9e",
|
|
4
|
+
"files": [
|
|
5
|
+
"modules/inventory/**",
|
|
6
|
+
"flowdular.json",
|
|
7
|
+
"platform/package.json",
|
|
8
|
+
"platform/src/generated/modules.server.ts",
|
|
9
|
+
"platform/src/generated/modules.client.ts",
|
|
10
|
+
"pnpm-lock.yaml"
|
|
11
|
+
],
|
|
12
|
+
"gates": [
|
|
13
|
+
"sandbox gates",
|
|
14
|
+
"pnpm --filter @flowdular/platform typecheck",
|
|
15
|
+
"pnpm verify"
|
|
16
|
+
],
|
|
17
|
+
"postMerge": [
|
|
18
|
+
"pnpm flowdular auth sync-scopes --module inventory.core --apply"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
# What CI runs (.github/workflows/ci.yml) plus the sandbox eject gates.
|
|
3
|
+
gates:
|
|
4
|
+
- id: sandbox-gates
|
|
5
|
+
command: eject stream runs spec-schema and module-schema once, then dependencies, typecheck, tests, format per draft module
|
|
6
|
+
required: true
|
|
7
|
+
appliesTo: sandbox-eject
|
|
8
|
+
- id: verify
|
|
9
|
+
command: pnpm verify
|
|
10
|
+
required: true
|
|
11
|
+
- id: build
|
|
12
|
+
command: pnpm build
|
|
13
|
+
required: true
|
|
14
|
+
- id: audit
|
|
15
|
+
command: pnpm audit --prod --audit-level high
|
|
16
|
+
required: true
|
|
17
|
+
- id: platform-typecheck
|
|
18
|
+
command: pnpm --filter @flowdular/platform typecheck
|
|
19
|
+
required: true
|
|
20
|
+
appliesTo: sandbox-eject
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": ["kind"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"kind": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["sandbox-eject", "pull-request", "tag"]
|
|
10
|
+
},
|
|
11
|
+
"moduleId": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)+$"
|
|
14
|
+
},
|
|
15
|
+
"sessionId": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 8
|
|
18
|
+
},
|
|
19
|
+
"version": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": ["blueprint", "branch", "files", "gates", "postMerge"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"blueprint": {
|
|
8
|
+
"const": "release@1.0.0"
|
|
9
|
+
},
|
|
10
|
+
"branch": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^(sandbox|feat|fix|core|release)/[a-z0-9.-]+$"
|
|
13
|
+
},
|
|
14
|
+
"files": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"minLength": 1
|
|
20
|
+
},
|
|
21
|
+
"uniqueItems": true
|
|
22
|
+
},
|
|
23
|
+
"gates": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": [
|
|
29
|
+
"pnpm verify",
|
|
30
|
+
"pnpm build",
|
|
31
|
+
"pnpm audit --prod --audit-level high",
|
|
32
|
+
"sandbox gates",
|
|
33
|
+
"pnpm --filter @flowdular/platform typecheck"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"uniqueItems": true
|
|
37
|
+
},
|
|
38
|
+
"postMerge": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
required: []
|
|
3
|
+
inCommit:
|
|
4
|
+
sandbox-eject:
|
|
5
|
+
- modules/{module}/**
|
|
6
|
+
- flowdular.json (module enable)
|
|
7
|
+
- platform/package.json (module enable)
|
|
8
|
+
- platform/src/generated/modules.server.ts (module sync)
|
|
9
|
+
- platform/src/generated/modules.client.ts (module sync)
|
|
10
|
+
- pnpm-lock.yaml (pnpm install, when changed)
|
|
11
|
+
pull-request:
|
|
12
|
+
- the reviewed change plus the CLI outputs it required
|
|
13
|
+
tag:
|
|
14
|
+
- nothing; the tag points at main
|
|
15
|
+
pullRequestBody:
|
|
16
|
+
- two sentences on what changed and why
|
|
17
|
+
- gate evidence: gate id, command, result
|
|
18
|
+
- post-merge step: pnpm flowdular auth sync-scopes --module <id> --apply against the deployment database
|
|
19
|
+
- no AI attribution footer, no em or en dashes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
steps:
|
|
3
|
+
- id: gates
|
|
4
|
+
role: module-executor
|
|
5
|
+
action: 'Sandbox: the six gates in the eject stream. Working tree: pnpm verify, pnpm build, pnpm audit --prod --audit-level high.'
|
|
6
|
+
inputs:
|
|
7
|
+
- 'working tree'
|
|
8
|
+
outputs:
|
|
9
|
+
- 'gate evidence'
|
|
10
|
+
- id: compose
|
|
11
|
+
role: module-executor
|
|
12
|
+
action: pnpm flowdular module enable <id> --apply for a new module (grants its scopes), pnpm flowdular module sync --apply otherwise; pnpm flowdular auth sync-scopes --module <id> --apply only to re-grant after a new permission.
|
|
13
|
+
inputs:
|
|
14
|
+
- 'modules/{module}/**'
|
|
15
|
+
outputs:
|
|
16
|
+
- 'flowdular.json'
|
|
17
|
+
- 'platform/package.json'
|
|
18
|
+
- 'platform/src/generated/*.ts'
|
|
19
|
+
- id: branch
|
|
20
|
+
role: module-executor
|
|
21
|
+
action: Branch sandbox/<module-dir>-<session-prefix> (or feat/, fix/, core/); commit only module files plus the CLI outputs; one logical change per commit.
|
|
22
|
+
inputs:
|
|
23
|
+
- 'diff'
|
|
24
|
+
outputs:
|
|
25
|
+
- 'branch'
|
|
26
|
+
- id: pull-request
|
|
27
|
+
role: module-executor
|
|
28
|
+
action: Short body, gate evidence, post-merge sync-scopes step, commands that produced generated files; no AI attribution.
|
|
29
|
+
inputs:
|
|
30
|
+
- 'branch'
|
|
31
|
+
- 'gate evidence'
|
|
32
|
+
outputs:
|
|
33
|
+
- 'pull request'
|
|
34
|
+
- id: review
|
|
35
|
+
role: reviewer
|
|
36
|
+
action: Reviewer checklist plus security-review when scopes or endpoints changed.
|
|
37
|
+
inputs:
|
|
38
|
+
- 'pull request'
|
|
39
|
+
outputs:
|
|
40
|
+
- 'review'
|
|
41
|
+
- id: post-merge
|
|
42
|
+
role: operator
|
|
43
|
+
action: Deploy; run pnpm flowdular auth sync-scopes --module <id> --apply with FD_AUTH_DATABASE pointing at the deployment; verify an owner sees the navigation entry. Tag v*.*.* to build the container.
|
|
44
|
+
inputs:
|
|
45
|
+
- 'merged main'
|
|
46
|
+
outputs:
|
|
47
|
+
- 'deployment'
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# security-review
|
|
2
|
+
|
|
3
|
+
Read-only review of a module, package or pull request against `.ai/skills/auth-security-review/SKILL.md`. The reviewer writes no production code; findings go back to the author with a severity, a location, a concrete failure scenario, the rule violated and the fix. Gates are the read-only greps and the tests the skill lists.
|
|
4
|
+
|
|
5
|
+
Run it for every change that adds or changes an endpoint, a scope, a token, a credential, or a CLI capability, and before a sandbox eject of a module that other tenants will use.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../packages/contracts/schemas/blueprint.schema.json",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"architecture": "0.2.0",
|
|
5
|
+
"owner": "platform-architecture",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"id": "security-review",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"risk": "read",
|
|
10
|
+
"agentRoles": ["reviewer"],
|
|
11
|
+
"executorProfiles": ["reviewer"],
|
|
12
|
+
"requiredReviewers": ["platform-owner"],
|
|
13
|
+
"requiresApprovedSpec": false
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"blueprint": "security-review@1.0.0",
|
|
3
|
+
"checksRun": ["grep -rn \"new ServerRoute\" modules/inventory/src"],
|
|
4
|
+
"findings": [
|
|
5
|
+
{
|
|
6
|
+
"severity": "blocker",
|
|
7
|
+
"claim": "Looks unsafe.",
|
|
8
|
+
"location": "modules/inventory/src/api/endpoints.ts",
|
|
9
|
+
"rule": "AGENTS.md rule 5",
|
|
10
|
+
"fix": "Rewrite it."
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"verdict": "changes-required"
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"blueprint": "security-review@1.0.0",
|
|
3
|
+
"checksRun": [
|
|
4
|
+
"grep -rn \"new ServerRoute\" modules/inventory/src",
|
|
5
|
+
"grep -rn tenantId modules/inventory/src/api",
|
|
6
|
+
"pnpm --filter @flowdular/module-inventory test"
|
|
7
|
+
],
|
|
8
|
+
"findings": [
|
|
9
|
+
{
|
|
10
|
+
"severity": "blocker",
|
|
11
|
+
"claim": "Tenant id is read from the request body.",
|
|
12
|
+
"location": "modules/inventory/src/api/endpoints.ts:41",
|
|
13
|
+
"scenario": "A member of tenant A posts { tenantId: \"B\" } and creates a location in tenant B.",
|
|
14
|
+
"rule": "AGENTS.md rule 6",
|
|
15
|
+
"fix": "Use principalFromContext(octane)!.tenantId and remove the body field."
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"verdict": "changes-required"
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
# Read-only checks. Nothing executes this file.
|
|
3
|
+
gates:
|
|
4
|
+
- id: raw-routes
|
|
5
|
+
command: grep -rn "new ServerRoute" modules/*/src | grep -v modules/auth
|
|
6
|
+
expect: no output
|
|
7
|
+
required: true
|
|
8
|
+
- id: tenant-from-input
|
|
9
|
+
command: grep -rn tenantId modules/*/src/api | grep -v principalFromContext
|
|
10
|
+
expect: no output
|
|
11
|
+
required: true
|
|
12
|
+
- id: unbounded-body
|
|
13
|
+
command: grep -rn "request.json()" modules/*/src
|
|
14
|
+
expect: no output outside modules/auth and modules/users
|
|
15
|
+
required: true
|
|
16
|
+
- id: public-endpoints
|
|
17
|
+
command: 'grep -rn "kind: ''public''" modules/*/src'
|
|
18
|
+
expect: each hit has a written reason
|
|
19
|
+
required: true
|
|
20
|
+
- id: tests
|
|
21
|
+
command: pnpm --filter @flowdular/module-{module} test
|
|
22
|
+
required: true
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": ["kind", "target"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"kind": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["module", "package", "pull-request"]
|
|
10
|
+
},
|
|
11
|
+
"target": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"minLength": 1
|
|
14
|
+
},
|
|
15
|
+
"diffRef": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": ["blueprint", "checksRun", "findings", "verdict"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"blueprint": {
|
|
8
|
+
"const": "security-review@1.0.0"
|
|
9
|
+
},
|
|
10
|
+
"checksRun": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"minItems": 1,
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1
|
|
16
|
+
},
|
|
17
|
+
"uniqueItems": true
|
|
18
|
+
},
|
|
19
|
+
"findings": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"items": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"required": [
|
|
25
|
+
"severity",
|
|
26
|
+
"claim",
|
|
27
|
+
"location",
|
|
28
|
+
"scenario",
|
|
29
|
+
"rule",
|
|
30
|
+
"fix"
|
|
31
|
+
],
|
|
32
|
+
"properties": {
|
|
33
|
+
"severity": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": ["blocker", "should-fix", "taste"]
|
|
36
|
+
},
|
|
37
|
+
"claim": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"minLength": 1
|
|
40
|
+
},
|
|
41
|
+
"location": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"minLength": 1
|
|
44
|
+
},
|
|
45
|
+
"scenario": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"minLength": 1
|
|
48
|
+
},
|
|
49
|
+
"rule": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"minLength": 1
|
|
52
|
+
},
|
|
53
|
+
"fix": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"verdict": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"enum": ["approved", "changes-required"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
# A review changes no spec. It checks that the spec and the code agree.
|
|
3
|
+
status:
|
|
4
|
+
allowed:
|
|
5
|
+
- approved
|
|
6
|
+
checks:
|
|
7
|
+
- permissions[].id equal the constants in src/acl/permissions.ts
|
|
8
|
+
- every endpoint permission appears in the spec
|
|
9
|
+
- a denial scenario and a tenant isolation scenario exist
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
steps:
|
|
3
|
+
- id: surface
|
|
4
|
+
role: reviewer
|
|
5
|
+
action: List every route in src/api/endpoints.ts and check access, resolveIdentity, sessionMutationDenial order, readJsonObject bounds, tenant source, bound SQL, error envelope.
|
|
6
|
+
inputs:
|
|
7
|
+
- 'target'
|
|
8
|
+
outputs:
|
|
9
|
+
- 'endpoint table'
|
|
10
|
+
- id: scopes
|
|
11
|
+
role: reviewer
|
|
12
|
+
action: Compare spec permissions, src/acl/permissions.ts, endpoint access and client scope; check whether auth sync-scopes is part of the landing.
|
|
13
|
+
inputs:
|
|
14
|
+
- 'spec/module.yaml'
|
|
15
|
+
- 'src/acl/permissions.ts'
|
|
16
|
+
outputs:
|
|
17
|
+
- 'scope findings'
|
|
18
|
+
- id: greps
|
|
19
|
+
role: reviewer
|
|
20
|
+
action: Run the greps in .ai/skills/auth-security-review/SKILL.md section 5 and record the output.
|
|
21
|
+
inputs:
|
|
22
|
+
- 'target'
|
|
23
|
+
outputs:
|
|
24
|
+
- 'grep evidence'
|
|
25
|
+
- id: tests
|
|
26
|
+
role: reviewer
|
|
27
|
+
action: Confirm the 401, 403, cross-tenant, CSRF and validation tests exist and fail when their guard is removed.
|
|
28
|
+
inputs:
|
|
29
|
+
- 'tests/'
|
|
30
|
+
outputs:
|
|
31
|
+
- 'test evidence'
|
|
32
|
+
- id: report
|
|
33
|
+
role: reviewer
|
|
34
|
+
action: Findings by severity with a concrete failure scenario each; verdict approved or changes-required; no code changes.
|
|
35
|
+
inputs:
|
|
36
|
+
- 'evidence'
|
|
37
|
+
outputs:
|
|
38
|
+
- 'review report'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Small, deliberately isolated files that show a shape. None of them is compiled or tested; `.ai/references/catalog` is the compiled reference for a module and `modules/auth/src/cli` for a CLI extension.
|
|
4
|
+
|
|
5
|
+
- `client-contribution/`: `createClientContribution(context)` entry, contribution factory with `csrfToken` threading, an `ICON_PATHS` glyph, unique ids, a `WORKSPACE_SLOTS` widget.
|
|
6
|
+
- `customer-cli-extension/`: `commands.json` catalog plus `defineCliExtension` implementation, metadata-identical.
|
|
7
|
+
- `bad/`: three negative examples, each with a README naming the violated `AGENTS.md` rule and the repair blueprint: `missing-acl` (raw route without access), `tenant-from-body` (tenant id from input, no session denial), `client-imports-server` (browser code importing the database repository).
|
|
8
|
+
- `module-create/task-packet.json`: the task packet shape validated by `validateTaskPacket` in `packages/harness/src/task-packet.ts`. No runtime consumes packets today; the sandbox works from briefs and role turns.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Bad example: client file importing a server module
|
|
2
|
+
|
|
3
|
+
`api.ts` sits under `src/client` and imports `../services/database-repository.ts`, which pulls in `@flowdular/sdk/database` and its `node:fs`, `node:path` and `node:async_hooks` imports. The Vite client build fails on the Node built-ins, and even where it compiled the browser would hold a database handle and a `tenantId` parameter chosen by the caller, bypassing every endpoint check.
|
|
4
|
+
|
|
5
|
+
Violated rules: `AGENTS.md` 5 and 6 (data reaches the client only through an endpoint that resolves identity and takes the tenant from the principal) and the layering in `.ai/skills/core-extend/SKILL.md` (client code imports `@flowdular/sdk/client`, `@flowdular/sdk/ui`, `octane`, `segment-state`, and the module's own `src/client` and `src/domain` types; never `src/services` or `src/server`).
|
|
6
|
+
|
|
7
|
+
Repair with the `bug-fix` blueprint: replace the import with a `fetch` in `src/client/api.ts` as in `.ai/references/catalog/src/client/api.ts`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export async function loadCustomers(): Promise<readonly Customer[]> {
|
|
11
|
+
const response = await fetch('/api/customers', {
|
|
12
|
+
headers: { accept: 'application/json' },
|
|
13
|
+
credentials: 'same-origin',
|
|
14
|
+
});
|
|
15
|
+
return (await payload<{ readonly customers: readonly Customer[] }>(response))
|
|
16
|
+
.customers;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The server endpoint supplies the tenant from the principal; the client never names one.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* WRONG: browser code importing the server repository. */
|
|
2
|
+
import type { DatabaseHandle } from '@flowdular/sdk/database';
|
|
3
|
+
import { DatabaseCustomerRepository } from '../services/database-repository.ts';
|
|
4
|
+
import type { Customer } from '../domain/types.ts';
|
|
5
|
+
|
|
6
|
+
declare const database: DatabaseHandle;
|
|
7
|
+
|
|
8
|
+
const repository = new DatabaseCustomerRepository(database);
|
|
9
|
+
|
|
10
|
+
export function loadCustomers(tenantId: string): Promise<readonly Customer[]> {
|
|
11
|
+
return repository.list(tenantId);
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Bad example: endpoint without access control
|
|
2
|
+
|
|
3
|
+
`endpoints.ts` mounts `/api/customers` with `new ServerRoute` from `@octanejs/app-core`. Nothing resolves an identity, nothing checks a permission, and `listAll()` has no tenant. Anyone who can reach the server reads every tenant's customers.
|
|
4
|
+
|
|
5
|
+
Violated rules: `AGENTS.md` 5 (every endpoint is `defineEndpoint` with `access: { kind: 'permission' }` and `resolveIdentity: endpointIdentityFromContext`) and 6 (tenant id from the principal). The security skill's first grep (`grep -rn "new ServerRoute" modules/*/src | grep -v modules/auth`) finds it.
|
|
6
|
+
|
|
7
|
+
Repair with the `bug-fix` blueprint (or `edit-module`, change class `endpoint`):
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const list = defineEndpoint({
|
|
11
|
+
id: 'customers.records.list',
|
|
12
|
+
path: '/api/customers',
|
|
13
|
+
methods: ['GET'],
|
|
14
|
+
access: { kind: 'permission', permission: CUSTOMER_PERMISSIONS.read },
|
|
15
|
+
resolveIdentity: endpointIdentityFromContext,
|
|
16
|
+
handler: ({ octane }) =>
|
|
17
|
+
jsonResponse({
|
|
18
|
+
customers: runtime.service().list(principalFromContext(octane)!.tenantId),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Reference: `.ai/references/catalog/src/api/endpoints.ts`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* WRONG: a raw ServerRoute with no permission and no identity resolver. */
|
|
2
|
+
import { ServerRoute } from '@octanejs/app-core';
|
|
3
|
+
import type { CustomerRuntime } from '../server/runtime.ts';
|
|
4
|
+
|
|
5
|
+
export function createCustomerRoutes(runtime: CustomerRuntime) {
|
|
6
|
+
const list = new ServerRoute({
|
|
7
|
+
path: '/api/customers',
|
|
8
|
+
methods: ['GET'],
|
|
9
|
+
handler: () => Response.json({ customers: runtime.service().listAll() }),
|
|
10
|
+
});
|
|
11
|
+
return [list] as const;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Bad example: tenant id from the request body
|
|
2
|
+
|
|
3
|
+
`endpoints.ts` has a permission and an identity resolver, and still lets a member of tenant A create a customer in tenant B by posting `{ "tenantId": "B", "name": "..." }`. The permission check passes because the principal holds `customers.records.manage` in its own tenant; the body decides where the row lands. The mutation also skips `sessionMutationDenial`, so a cross-site form post with a valid cookie would be accepted.
|
|
4
|
+
|
|
5
|
+
Violated rules: `AGENTS.md` 6 (tenant id only from `principalFromContext(octane)!.tenantId`) and 7 (`sessionMutationDenial(octane, auth)` first on every mutation). The security skill's grep `grep -rn tenantId modules/*/src/api | grep -v principalFromContext` finds it.
|
|
6
|
+
|
|
7
|
+
Repair with the `bug-fix` blueprint: remove the body field, take `auth: AuthRuntime` in `createCustomerRoutes`, and write
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
handler: async ({ octane }) => {
|
|
11
|
+
const denial = sessionMutationDenial(octane, auth);
|
|
12
|
+
if (denial) return denial;
|
|
13
|
+
const value = await readJsonObject(octane.request);
|
|
14
|
+
const tenantId = principalFromContext(octane)!.tenantId;
|
|
15
|
+
...
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Add the test from `.ai/skills/test-hardening/SKILL.md`: a principal of tenant A posting `tenantId: 'B'` still creates in A (the field is ignored or rejected with 400).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* WRONG: the tenant id is read from the request body. */
|
|
2
|
+
import {
|
|
3
|
+
defineEndpoint,
|
|
4
|
+
jsonResponse,
|
|
5
|
+
readJsonObject,
|
|
6
|
+
requiredString,
|
|
7
|
+
} from '@flowdular/sdk/server';
|
|
8
|
+
import { endpointIdentityFromContext } from '@flowdular/sdk/modules/auth/server';
|
|
9
|
+
import { CUSTOMER_PERMISSIONS } from '../acl/permissions.ts';
|
|
10
|
+
import type { CustomerRuntime } from '../server/runtime.ts';
|
|
11
|
+
|
|
12
|
+
export function createCustomerRoutes(runtime: CustomerRuntime) {
|
|
13
|
+
const create = defineEndpoint({
|
|
14
|
+
id: 'customers.records.create',
|
|
15
|
+
path: '/api/customers',
|
|
16
|
+
methods: ['POST'],
|
|
17
|
+
access: { kind: 'permission', permission: CUSTOMER_PERMISSIONS.manage },
|
|
18
|
+
resolveIdentity: endpointIdentityFromContext,
|
|
19
|
+
handler: async ({ octane }) => {
|
|
20
|
+
const value = await readJsonObject(octane.request);
|
|
21
|
+
const tenantId = requiredString(value, 'tenantId');
|
|
22
|
+
return jsonResponse(
|
|
23
|
+
{
|
|
24
|
+
customer: runtime
|
|
25
|
+
.service()
|
|
26
|
+
.create(tenantId, { name: requiredString(value, 'name') }),
|
|
27
|
+
},
|
|
28
|
+
201,
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return [create.serverRoute] as const;
|
|
33
|
+
}
|