Validation Contract — Single Source of Truth¶
Audience: Contributors | Status: Stable — Internal | Verified against: v0.43.0
Covers issues #23, #24, and #357.
This document defines the canonical validation rule contract for CloudBlocks. All frontend and backend validation must derive from this specification. No layer may invent rules that are not listed here.
1. Ownership and Authority¶
| Aspect | Policy |
|---|---|
| Rule owner | This document (docs/design/VALIDATION_CONTRACT.md) |
| Change process | PR with updates to this doc + corresponding FE/BE code changes in the same PR |
| Versioning | Semantic — bump this document's Current version when adding/removing/changing rules |
| Current version | 1.1.0 |
2. Rule Schema¶
Every validation rule is identified by a unique ruleId and produces a ValidationError:
interface ValidationError {
ruleId: string; // Unique rule identifier (e.g., "rule-db-private")
severity: 'error' | 'warning';
message: string; // Human-readable description
suggestion: string; // Actionable fix suggestion
targetId: string; // ID of the entity that violates the rule
}
3. Placement Rules (Legacy Category-based)¶
Placement rules validate that blocks are placed on appropriate container blocks.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-container-block-exists |
error | Block has no placementId or container block not found |
Block is not placed on any container block |
rule-compute-subnet |
error | Compute block not on a subnet container block |
Compute block must be placed on a Subnet container block |
rule-db-private |
error | Database block not on a subnet container block with subnetAccess: "private" |
Database block must be placed on a private Subnet container block |
rule-gw-public |
error | Gateway block not on a subnet container block with subnetAccess: "public" |
Gateway block must be placed on a public Subnet container block |
rule-storage-subnet |
error | Storage block not on a subnet container block |
Storage block must be placed on a Subnet container block |
rule-analytics-subnet |
error | Analytics block not on a subnet container block |
Analytics block must be placed on a Subnet container block |
rule-identity-subnet |
error | Identity block not on a subnet container block |
Identity block must be placed on a Subnet container block |
rule-observability-subnet |
error | Observability block not on a subnet container block |
Observability block must be placed on a Subnet container block |
rule-serverless-network |
error | function, queue, or event block not on a region container block |
Serverless blocks (function/queue/event) must be placed on a Region container block |
3.1 v2.0 Layer Hierarchy Rules¶
These rules are defined for the next-generation layer system but are not yet wired into the main engine.ts orchestrator.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-layer-hierarchy |
error | Block placed on a container block that is not a valid parent layer | Invalid layer hierarchy for this block type |
rule-grid-alignment |
error | Block position not CU-aligned (integer coordinates) | Block must be aligned to the grid |
rule-no-overlap |
error | Block overlaps with sibling on same container block (AABB detection) | Block cannot overlap with other blocks |
4. Connection Rules¶
Connection rules validate dataflow between blocks. Connections follow initiator semantics — the source is the client/initiator, the target is the server/receiver.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-conn-source |
error | Source endpoint ID not found in blocks or external actors | Connection source not found |
rule-conn-target |
error | Target endpoint ID not found in blocks or external actors | Connection target not found |
rule-conn-self |
error | sourceId === targetId |
A block cannot connect to itself |
rule-conn-invalid |
error | Source → Target pair not in allowed connections map | Invalid connection: {source} → {target} |
Allowed Connection Map¶
| Source (Initiator) | Allowed Targets (Receiver) |
|---|---|
internet |
gateway |
gateway |
compute, function |
compute |
database, storage, analytics, identity, observability |
function |
storage, database, queue |
queue |
function |
event |
function |
database, storage, analytics, identity, and observability are receiver-only. queue and event can only connect to function.
Implementation References¶
- Frontend:
apps/web/src/entities/validation/connection.ts - Backend: Not yet implemented (planned for Milestone 6 server-side validation)
5. Aggregation Rules¶
Validation for block aggregation (scaling/clustering) configuration.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-aggregation-count |
error | block.aggregation.count < 1 or count is not an integer |
Aggregation count must be a positive integer |
Implementation References¶
- Frontend:
apps/web/src/entities/validation/aggregation.ts - Backend: Not yet implemented (planned for Milestone 6)
5.1 Application Placement Rules (Planned — Not Yet Implemented)¶
Status: Planned. The Application entity and its placement rules are designed but have no corresponding implementation. The rules below describe the intended behavior for a future milestone.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-aggregation-count |
error | Aggregation count < 1 or non-integer | Block has invalid aggregation count (must be >= 1 integer) |
Aggregation Behavior¶
- If a block has no
aggregationfield, it is treated as a single instance (valid). - Aggregation count must be a positive integer (≥ 1).
- Block size remains fixed regardless of count — aggregation is visual only (badge "×N").
Implementation References (Application)¶
- Frontend:
apps/web/src/entities/validation/aggregation.ts - Backend: Not yet implemented (planned for Milestone 6)
6. Role Rules¶
Validation for identity and access management roles.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-role-invalid |
error | Role not in BLOCK_ROLES list |
Invalid role assigned to block |
rule-role-duplicate |
warning | Same role appears more than once on a block | Duplicate role assigned |
7. Provider Validation Rules (Warnings)¶
Specific rules based on cloud provider characteristics.
| Rule ID | Severity | Condition | Message |
|---|---|---|---|
rule-provider-aws-lambda-subnet |
warning | AWS Lambda placed on a subnet (may not need VPC) | AWS Lambda may not require a subnet placement unless VPC access is needed |
rule-provider-gcp-sql-public |
warning | GCP Cloud SQL on a public subnet | GCP Cloud SQL is usually restricted to private access |
rule-provider-unknown-subtype |
warning | Block subtype not in known provider subtypes | Unknown resource subtype for this provider |
Known Subtypes Table¶
Provider validation uses the internal KNOWN_SUBTYPES map to verify resource alignment.
8. Orchestration¶
The validation engine iterates all blocks and connections, collecting errors and warnings into a single ValidationResult through 5 distinct passes:
- Placement Pass (
placement.ts) - Aggregation Pass (
aggregation.ts) - Role Pass (
role.ts) - Connection Pass (
connection.ts) - Provider Pass (
providerValidation.ts)
interface ValidationResult {
valid: boolean; // true when errors.length === 0
errors: ValidationError[];
warnings: ValidationError[];
}
Implementation References¶
- Frontend:
- Orchestrator:
apps/web/src/entities/validation/engine.ts - Rules:
placement.ts,connection.ts,aggregation.ts,role.ts,providerValidation.ts - v2.0 layer rules: defined in
placement.ts(not yet wired into engine.ts) - Backend: Not yet implemented (planned for Milestone 6 server-side validation)
9. FE/BE Alignment Contract¶
Current State (Milestone 5)¶
- Frontend: Full validation engine implemented in TypeScript (
entities/validation/) - Backend: No server-side validation. The backend is a thin orchestration layer that trusts frontend-validated data.
Future State (Milestone 6+)¶
When backend validation is introduced:
- Rule definitions must be generated from this document — no hand-written rule divergence.
- Compatibility tests must exist: a shared JSON test fixture file (
tests/fixtures/validation-cases.json) containing input architectures and expected validation results. Both FE and BE test suites must consume this file. - Rule additions: Add to this document first, then implement in both layers in the same PR.
- Rule changes: Update this document, update both implementations, update shared fixtures.
Compatibility Test Format¶
{
"contractVersion": "1.1.0",
"cases": [
{
"name": "database-on-public-subnet",
"input": { "blocks": [...], "containerBlocks": [...], "connections": [...] },
"expected": {
"valid": false,
"errors": [{ "ruleId": "rule-db-private", "targetId": "block-db01" }]
}
}
]
}
10. Migration Notes¶
- Serverless block categories (
FunctionBlock,QueueBlock,EventBlock) are implemented and reflected in the placement rules above. - When adding new connection types (e.g.,
EventFlow), update the allowed connection map here first. - Breaking rule changes (removing a rule, changing severity) require a version bump to this document's Current version field.