# TenantPilot Constitution ## Core Principles ### Inventory-first, Snapshots-second - All modules MUST operate primarily on Inventory as “last observed” state. - Inventory is the source of truth for what TenantPilot last observed; Microsoft Intune remains the external truth. - Snapshots/Backups MUST be explicit actions (manual or scheduled) and MUST remain immutable. ### Read/Write Separation by Default - Analysis, reporting, and monitoring features MUST be read-only by default. - Any write/change function (restore, remediation, promotion) MUST include preview/dry-run, explicit confirmation, audit logging, and tests. - High-risk policy types default to `preview-only` restore unless explicitly enabled by a feature spec + tests. ### Single Contract Path to Graph - All Microsoft Graph calls MUST go through `GraphClientInterface`. - Object types and endpoints MUST be modeled first in the contract registry (`config/graph_contracts.php`). - Feature code MUST NOT hardcode “quick endpoints” or bypass contracts. - Unknown/missing policy types MUST fail safe (preview-only / no Graph calls) rather than guessing endpoints. ### Deterministic Capabilities - Backup/restore/risk/support flags MUST be derived deterministically from config/contracts via a Capabilities Resolver. - The resolver output MUST be programmatically testable (snapshot/golden tests) so config changes cannot silently break behavior. ### Tenant Isolation is Non-negotiable - Every read/write MUST be tenant-scoped. - Cross-tenant views (MSP/Platform) MUST be explicit, access-checked, and aggregation-based (no ID-based shortcuts). - Prefer least-privilege roles/scopes; surface warnings when higher privileges are selected. - A non-member attempting to access a tenant route MUST be deny-as-not-found (404). ### RBAC Standard (RBAC-001) RBAC-001 Two Planes - The platform MUST maintain two strictly separated authorization planes: - Tenant plane (`/admin/t/{tenant}`): authenticated Entra users (`users`), authorization is tenant-scoped. - Platform plane (`/system`): authenticated platform users (`platform_users`), authorization is platform-scoped. - Cross-plane access MUST be deny-as-not-found (404) (not 403) to avoid route enumeration. RBAC-002 Capabilities-first Authorization - Feature code MUST NOT check raw roles directly (e.g. string role comparisons). - Feature code MUST check capabilities via Gates/Policies only. - A canonical capability registry MUST exist as the single source of truth (e.g. `TenantCapabilities` / `PlatformCapabilities`). - Role → capability mapping MUST reference only registry entries. RBAC-003 Least Privilege Role Semantics - Tenant roles MUST follow least-privilege semantics: - Readonly: view-only; MUST NOT start operations and MUST NOT mutate data. - Operator: MAY start allowed tenant operations; MUST NOT manage credentials, settings, members, or perform destructive actions. - Manager: MAY manage tenant configuration and start operations; MUST NOT manage tenant memberships (Owner-only). - Owner: MAY manage memberships and all tenant configuration; Owner-only “danger zone” actions MUST remain Owner-only. RBAC-004 UI is not Security - Hiding UI elements is NOT sufficient. - Every mutation endpoint and action MUST enforce authorization server-side (Policy/Gate). RBAC-005 Destructive Actions Gate - All destructive actions (delete / force delete / irreversible operations) MUST: - require an explicit confirmation (e.g., `requiresConfirmation()` or equivalent), - be protected by a Policy/Gate, - have at least one regression test asserting the action is forbidden for non-authorized roles. RBAC-006 Membership Safety Rule - The system MUST prevent removing or demoting the last remaining Owner of a tenant. RBAC-007 Tenant Isolation - All tenant-plane queries MUST be tenant-scoped. - A non-member attempting to access a tenant route MUST be deny-as-not-found (404). RBAC-008 Auditing - All access-control relevant changes MUST write `AuditLog` entries with stable action IDs, including: - membership add / role change / remove - provider credential rotation / connection disable - break-glass enter / exit / expire (platform plane) - `AuditLog` entries MUST be redacted (no secrets/tokens, minimal identity fields). RBAC-009 Testability Gate - Any new feature that introduces or changes authorization MUST include: - at least one positive test (authorized user can do it), - at least one negative test (unauthorized user cannot do it), - and MUST NOT introduce role-string checks outside the central mapping/registry. ### Operations / Run Observability Standard - Every long-running or operationally relevant action MUST be observable, deduplicated, and auditable via Monitoring → Operations. - An action MUST create/reuse a canonical `OperationRun` and execute asynchronously when any of the following applies: 1. It can take > 2 seconds under normal conditions. 2. It performs remote/external calls (e.g., Microsoft Graph). 3. It is queued or scheduled. 4. It is operationally relevant for troubleshooting/audit (“what ran, who started it, did it succeed, what failed?”). - Actions that are DB-only and typically complete in < 2 seconds MAY skip `OperationRun`. - OPS-EX-AUTH-001 — Auth Handshake Exception: - OIDC/SAML login handshakes MAY perform synchronous outbound HTTP (e.g., token exchange) without an `OperationRun`. - Rationale: interactive, session-critical, and not a tenant-operational “background job”. - Guardrail: outbound HTTP for auth handshakes is allowed only on `/auth/*` endpoints and MUST NOT occur on Monitoring/Operations pages. - If an action is security-relevant or affects operational behavior (e.g., “Ignore policy”), it MUST write an `AuditLog` entry including actor, tenant, action, target, before/after, and timestamp. - The `OperationRun` record is the canonical source of truth for Monitoring (status, timestamps, counts, failures), even if implemented by multiple jobs/steps (“umbrella run”). - “Single-row” runs MUST still use consistent counters (e.g., `total=1`, `processed=0|1`) and outcome derived from success/failure. - Monitoring pages MUST be DB-only at render time (no external calls). - Start surfaces MUST NOT perform remote work inline; they only: authorize, create/reuse run (dedupe), enqueue work, confirm + “View run”. - Active-run dedupe MUST be enforced at DB level (partial unique index/constraint for active states). - Failures MUST be stored as stable reason codes + sanitized messages; never persist secrets/tokens/PII/raw payload dumps in failures or notifications. - Graph calls are allowed only via explicit user interaction and only when delegated auth is present; never as a render side-effect (restore group mapping is intentionally DB-only). - Monitoring → Operations is reserved for `OperationRun`-tracked operations. - Scheduled/queued operations MUST use locks + idempotency (no duplicates). - Graph throttling and transient failures MUST be handled with backoff + jitter (e.g., 429/503). ### Data Minimization & Safe Logging - Inventory MUST store only metadata + whitelisted `meta_jsonb`. - Payload-heavy content belongs in immutable snapshots/backup storage, not Inventory. - Logs MUST not contain secrets/tokens; monitoring MUST rely on run records + error codes (not log parsing). ### Badge Semantics Are Centralized (BADGE-001) - Status-like badges (status/outcome/severity/risk/availability/boolean signals) MUST render via `BadgeCatalog` / `BadgeRenderer`. - Filament resources/pages/widgets/views MUST NOT introduce ad-hoc status-like badge mappings (use a `BadgeDomain` instead). - Introducing or changing a status-like value MUST include updating the relevant badge mapper and adding/updating tests for the mapping. - Tag/category chips (e.g., type/platform/environment) are not status-like and are not governed by BADGE-001. ### Spec-First Workflow - For any feature that changes runtime behavior, include or update `specs/-/` with `spec.md`, `plan.md`, `tasks.md`, and `checklists/requirements.md`. - New work branches from `dev` using `feat/-` (spec + code in the same PR). ## Quality Gates - Changes MUST be programmatically tested (Pest) and run via targeted `php artisan test ...`. - Run `./vendor/bin/sail bin pint --dirty` before finalizing. ## Governance ### Scope & Compliance - This constitution applies across the repo. Feature specs may add stricter constraints but not weaker ones. - Restore semantics changes require: spec update, checklist update (if applicable), and tests proving safety. ### Amendment Procedure - Propose changes as a PR that updates `.specify/memory/constitution.md`. - The PR MUST include a short rationale and list of impacted templates/specs. - Amendments MUST update **Last Amended** date. ### Versioning Policy (SemVer) - **PATCH**: clarifications/typos/non-semantic refinements. - **MINOR**: new principle/section or materially expanded guidance. - **MAJOR**: removing/redefining principles in a backward-incompatible way. **Version**: 1.5.0 | **Ratified**: 2026-01-03 | **Last Amended**: 2026-01-27