TenantAtlas/specs/061-provider-foundation/plan.md

8.9 KiB

Implementation Plan: Provider Foundation v1 (Microsoft-first, Security-first)

Branch: 061-provider-foundation | Date: 2026-01-24 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

  • Introduce tenant-scoped Microsoft provider connections (multiple allowed) with a required default connection and isolated credentials.
  • Centralize all Microsoft Graph access through the existing Graph contract path (GraphClientInterface + config/graph_contracts.php) while standardizing provider operation context and failure handling.
  • Add provider capability interfaces (health check, inventory, compliance, placeholders) so future provider modules can be introduced without scattering provider-specific logic.
  • Run all provider operations asynchronously as canonical OperationRuns with clear UX rules: same-operation dedupe returns the active run; different-operation requests are blocked as “scope busy” with a link to the active run.
  • Guarantee DB-only render for Provider Connections and Monitoring surfaces; outbound calls occur only in queued jobs.
  • Add regression tests for tenant isolation, role authorization, dedupe/scope-busy behavior, and secret redaction in run failures.

Technical Context

Language/Version: PHP 8.4 (Laravel 12)
Primary Dependencies: Laravel 12 + Filament v5 + Livewire v4
Storage: PostgreSQL (Sail) with JSONB where appropriate
Testing: Pest (PHPUnit)
Target Platform: Web application (Sail/Docker locally; container deploy via Dokploy)
Project Type: web
Performance Goals: DB-only pages render fast (no outbound calls); operation start surfaces respond quickly after enqueueing a run
Constraints: No outbound HTTP during render/poll/hydration; max 1 active provider run per Entra tenant scope; secrets/tokens never persisted in runs/logs. Remote provider calls are read-only (Graph fetches), but the platform will persist local state (snapshots/summary_counts/health) as DB writes.
Scale/Scope: Multiple suite tenants; multiple Microsoft connections per tenant; operations history grows over time (indexing required)

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first, snapshots-second: PASS (provider inventory is treated as “last observed”; no backup/restore semantics introduced in v1).
  • Read/write separation: PASS (v1 provider operations perform remote reads only, while persisting local DB state such as health and snapshot summaries. Any future remote writes (e.g., script deploy/app publish) remain out of scope and would require preview + confirmation + audit + tests).
  • Single contract path to Graph: PASS (all Microsoft Graph calls stay behind GraphClientInterface and are modeled in config/graph_contracts.php; no ad-hoc HTTP in UI/render).
  • Deterministic capabilities: PASS (provider operations are registered centrally and validated/tested; no “quick endpoints” scattered across features).
  • Tenant isolation: PASS (provider connections, credentials, and runs are scoped to the current Suite Tenant; cross-tenant access is denied-as-not-found).
  • Operations / run observability standard: PASS (all provider operations create/reuse a canonical OperationRun; start surfaces enqueue-only; Monitoring remains DB-only).
  • Automation (locks + idempotency): PASS (same-operation starts dedupe to the active run; different-operation starts for the same scope are blocked as “scope busy”; Graph throttling handled via existing retry/backoff+jitter).
  • Data minimization & safe logging: PASS (no secrets/tokens persisted or logged; failures use stable reason codes + sanitized short messages).
  • Badge semantics (BADGE-001): PASS (any status-like UI badges for connection status/health must render via BadgeCatalog / BadgeRenderer with tests; otherwise status is shown as plain text).

Project Structure

Documentation (this feature)

specs/061-provider-foundation/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)

Source Code (repository root)

app/
├── Filament/
├── Jobs/
├── Models/
├── Policies/
├── Services/
│   ├── Graph/
│   └── Operations/
└── Support/

config/
database/
resources/
routes/
tests/

Structure Decision: Single Laravel web application with Filament admin panel; provider foundation lives in app/Models, app/Services, app/Jobs, app/Policies, and integrates with the existing Graph + Operations infrastructure.

Complexity Tracking

No constitution violations required for this feature.

Phase 0 — Research (output: research.md)

See: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/research.md

Goals:

  • Confirm the existing Graph contract path and reuse it (no new bespoke HTTP client in UI/features).
  • Confirm token acquisition is app-only (client credentials) and is sourced from provider_credentials via CredentialManager inside ProviderGateway (no secrets in config/env beyond bootstrap).
  • Ensure provider operations supply auth context to GraphClientInterface via ProviderGateway (the only decryptor), not from UI/service layers.
  • Confirm GraphClientInterface binding can be enabled without env secrets when ProviderGateway supplies per-request credentials (e.g., via a GRAPH_ENABLED override).
  • Confirm existing OperationRun idempotency patterns and align provider operations with them.
  • Confirm existing sanitization / reason-code utilities for run failures and reuse them.

Phase 1 — Design & Contracts (outputs: data-model.md, contracts/, quickstart.md)

See:

  • /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/data-model.md
  • /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/contracts/
  • /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/quickstart.md

Design focus:

  • Data model for provider connections + isolated credentials with a required default connection.
  • Credential isolation: provider_credentials stores encrypted payload; only ProviderGateway/CredentialManager can decrypt; never expose in resources/logs/runs.
  • Provider module contracts: define capability interfaces and ensure Microsoft implementations depend on ProviderGateway rather than calling Graph directly.
  • OperationRun identity + scope-busy behavior consistent with clarified UX rules.
  • Explicit Graph contract registry updates required for new provider operations.

Phase 2 — Implementation Outline (tasks created in /speckit.tasks)

  • Schema: create provider connection + credential tables, indexes, and state fields.
  • Authorization (capabilities-first): implement gates/policies provider.view, provider.manage, provider.run.
  • Owner/Manager: view+manage+run; Operator: view+run; Readonly: view only.
  • No role == X checks in feature code; enforce via policies/gates + tests.
  • Provider foundation services: connection resolution (default/explicit), credential resolution, and operation registry.
  • Provider gateway + modules: implement ProviderGateway, CredentialManager, and capability interfaces used by all provider operations.
  • Operations: implement health check, inventory collection (minimal), and compliance snapshot (counts) as queued OperationRuns.
  • Concurrency: same-operation dedupe to active run; different-operation blocked as “scope busy” with active-run link; enforce race-safety with a DB lock/transaction around start-gate decisions.
  • UI: Provider Connections management is DB-only at render; “Check connection” and any provider operations are enqueue-only (jobs perform outbound HTTP). Add a DB-only render test with Http::fake() asserting zero outbound requests during render/poll/hydration.
  • Guardrails/tests: DB-only render tests, secret-redaction tests, reason-code mapping tests, concurrency behavior tests.

Constitution Check (Post-Design)

Re-check result: PASS. Design artifacts explicitly keep Graph calls behind GraphClientInterface + contract registry and standardize provider operations via OperationRun with tenant isolation, idempotency, and safe failure handling.