Commit graph

26 commits

Author SHA256 Message Date
Tyler J King
5015f3dd43 fix(drivers): JWKS verification for Keycloak, remove Entra fallback, gate on_behalf_of
C-1: Keycloak driver now verifies JWT signatures via JWKS.
     Forged tokens are rejected. Previously any base64 JWT was accepted.
C-2: on_behalf_of requires gsap:impersonate role in JWT claims.
C-3: Entra driver denies on JWKS failure (no unverified fallback).
H-10: JWKS cache refreshes on kid miss for key rotation.

Shared JWKSVerifier used by both drivers. alg=none blocked.
iss, aud, exp validated for all tokens.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 07:51:38 -04:00
Tyler J King
4dff879c84 feat: wire credential resolver and connectors into broker startup
All connectors registered conditionally based on settings.
CredentialResolver with Entra backend (production) or Stub
backend (dev mode). 15 new tests covering credential resolution,
session lifecycle, orchestrator workflows, and device routing.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 06:03:57 -04:00
Tyler J King
5adc55aff5 feat(routing): add DeviceRouter for automatic connector selection
Routes operations to Intune, PowerShell, Bascule, or Ansible
based on operation type and target device OS. API-mediated ops
always go to Intune, fleet ops to Ansible, session ops routed
by OS. Single invoke endpoint for all device operations.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 06:01:55 -04:00
Tyler J King
2ac5aa3b85 feat(connectors): add OrchestratorConnector base and stubbed Ansible
Multi-step workflow base class with plan/execute lifecycle and
partial-completion reporting. Ansible connector stubbed —
ansible-runner integration in future sprint. Credentials
resolved per-host at runtime via CredentialResolver, never stored.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 06:00:48 -04:00
Tyler J King
eee8740ce8 feat(connectors): add stubbed Bascule and PowerShell connectors
Bascule: session-based connector using AC as credential.
Transport stubbed — Shellstream integration in future sprint.

PowerShell: session-based connector using Kerberos credentials
from CredentialResolver. PSRP transport stubbed — pypsrp
integration in future sprint.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:59:56 -04:00
Tyler J King
5a759f5e12 feat(connectors): add SessionTransport and SessionConnector base
Session-based connectors acquire credentials at invocation time
from CredentialResolver, manage transport lifecycle with cleanup
guarantees, and never store credentials.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:58:58 -04:00
Tyler J King
24eefe1699 feat(credentials): add Entra and Stub credential backends
Entra backend resolves OAuth tokens via MSAL client_credentials
(OBO flow wired in future sprint) and passes ACs through for
Bascule. Kerberos stubbed pending hybrid environment config.
Stub backend for dev/testing without real IdP.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:57:52 -04:00
Tyler J King
043693652a feat(credentials): add CredentialResolver and Credential types
Zero-credential-storage architecture. The broker holds ACs
(authorization). Pluggable CredentialBackend implementations
hold secrets. Transports acquire short-lived, scoped credentials
at invocation time and discard after use.

Credential types: BasculeCredential, KerberosCredential,
OAuthCredential, SSHCertCredential.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:57:00 -04:00
Tyler J King
1d24019544 docs: add Intune connector configuration guide
Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:30:06 -04:00
Tyler J King
6cfe5f7d9a test: add Graph API client unit tests
Verifies MSAL token acquisition, error handling, and
Authorization header inclusion in Graph API requests.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:28:46 -04:00
Tyler J King
e24a87db6f feat(mcp): add Intune device management tools
MCP tools for list_devices, get_device_compliance, sync_device,
remote_lock. All route through governed IntuneConnector
invocation with Chronicle audit.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:25:08 -04:00
Tyler J King
03a99b4aff feat(authorize): add Intune compliance-gated AC issuance
AC issuance can now require device compliance via Intune.
Configurable per-accord and globally. Disabled by default
for backward compatibility. Emits DEVICE_COMPLIANCE_CHECKED
Chronicle event. Adds device_id, device_compliant, and
compliance_checked_at fields to AuthorizationContext.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:24:03 -04:00
Tyler J King
871541f0eb feat(connectors): add Intune device management connector
Implements ConnectorPlugin for Intune Graph API operations.
Governed invocation: every Intune call requires an active AC
and emits a Chronicle CONNECTOR_INVOKED event.
Operations: list, get, compliance check, sync, lock, retire, wipe.
In-memory compliance cache with configurable TTL.
Conditional registration via intune_enabled setting.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:21:47 -04:00
Tyler J King
8196396ce6 feat(drivers): add native Entra identity driver
Validates Entra JWTs directly via JWKS verification.
Extracts device_id for compliance gating, MFA status,
roles, and constructs DID from Entra tenant + oid.
Adds device_id field to AuthResult dataclass.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:19:54 -04:00
Tyler J King
1ab47417c9 refactor: extract shared Graph API client from Entra registrar
Creates gsap_broker/intune/graph_client.py with MSAL
client_credentials auth and typed Graph API methods.
Entra registrar refactored to consume the shared client.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:16:09 -04:00
8c949b38c0 docs: M6.2 TODO on Chronicle webhook poster — replace with gRPC RecordEvent
Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-08 13:49:18 -04:00
f7c49387c1 feat: absorb llm-principal-broker as gsap_broker/delegations/
Merges the standalone llm-principal-broker (1,132 LOC) into fastapi-gsap
as an in-process module. The previous architecture had two FastAPI
processes where the broker called GSAP over HTTP on every delegation
creation; now the lifecycle code uses GSAP's own async DB engine
directly and inserts AuthorizationContextDB rows in the same
transaction context.

New module: gsap_broker/delegations/
  models.py             Pydantic request/response shapes
  storage.py            DelegationDB SQLModel sharing the GSAP engine
  lifecycle.py          DelegationManager — in-process AC issuance via
                        AuthorizationContextDB.insert (no HTTP self-call)
  cleanup.py            30s background task for stale delegations
  router.py             /delegations/* FastAPI router (4 endpoints)
  registrars/
    base.py             AgentRegistrar Protocol + AgentCredentials
    stub.py             dev-mode no-op
    keycloak.py         Keycloak Admin REST API
    entra.py            Microsoft Entra Agent ID via Graph (lazy import)
    factory.py          driver selection (auto/stub/keycloak/entra)

Wiring:
  app.py mounts the delegations router and starts the cleanup task in
  the existing lifespan context manager.
  settings.py absorbs the keycloak_admin_*, entra_*, and
  agent_registrar fields from the old broker's settings.
  pyproject.toml adds an optional `entra` extra for the msal dep.

Behaviour preservation:
  - Endpoints kept identical: POST /, POST /{id}/revoke, GET /{id}, GET /
  - Chronicle event codes preserved: 0x3001 / 0x3003 / 0x3004
  - DelegationScope defaults unchanged (max_ttl_minutes=60, max_commands=500)
  - Capability ceiling -> capability_mask conversion documented inline

Smoke test: `python -c "from gsap_broker.app import app"` loads cleanly
with 26 routes including the four /delegations/ endpoints.

The standalone llm-principal-broker repo is archived to
~/projects/archive/llm-principal-broker.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-08 13:37:06 -04:00
3ed75169c7 feat: MCP endpoint — governance primitives as MCP tools
POST /mcp — Streamable HTTP JSON-RPC 2.0 (MCP spec 2024-11-05)

11 governance tools for consortia builders:
  request_ac          — AC issuance (wraps /governance/authorize/)
  validate_ac         — AC validation (wraps /governance/authorize/{token}/)
  post_cr             — CR posting (wraps /governance/complete/)
  query_accord        — AccordTemplate lookup
  request_delegation  — proxy to LLM Principal Broker
  revoke_delegation   — proxy to LLM Principal Broker
  get_delegation      — proxy to LLM Principal Broker
  list_agents         — proxy to LLM Principal Broker
  get_posture         — DEFCON level and restrictions (30s cache)
  check_operation     — dry-run operation check against posture
  session_info        — current session context

Tool handlers call existing broker internals — no logic duplication.
Delegation tools proxy to LLM Principal Broker via HTTP.
Every tool call recorded in Chronicle (MCP_TOOL_CALL 0x3020).

Any MCP-compatible agent can discover and use governance operations
through standard protocol — no Capstone, no Django required.

All 7 smoke tests pass (init, list, posture, check_op, session, accord, error).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 18:42:29 -04:00
Tyler J King
590c267f03 feat: on_behalf_of — Bascule asserts operator identity in AC requests
When a trusted caller (Bascule SA) sets on_behalf_of in the authorize
request, the AC principal is the specified operator DID instead of the
SA's JWT identity. Display name extracted from last DID segment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 09:06:45 -04:00
Tyler J King
295fe55bf2 feat: session-scoped ACs — multiple CRs per session
Adds session_mode flag to AC lifecycle. When session_mode=true:
- AC transitions to 'active' (not 'consumed') on first CR
- Stays active for subsequent CRs during the session
- 'session_end' outcome transitions AC to 'consumed'
- Non-session ACs behave as before (consumed on first CR)

Schema:
- ACStatus: add ACTIVE enum value
- Outcome: add SESSION_END enum value
- AuthorizeRequest: add session_mode bool field
- AuthorizationContextDB: add session_mode column
- Auto-migration via ALTER TABLE on startup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 02:06:15 -04:00
Tyler J King
5ac577af19 fix: CR endpoint — greenlet bug + UUID format + missing column
Three bugs in the complete handler:

1. SQLAlchemy greenlet: ORM model attribute access triggers sync
   lazy-loads in async context. Fix: raw SQL via text() for all
   DB operations in the CR handler.

2. UUID format: SQLite stores UUIDs without hyphens (via SQLModel).
   Raw SQL comparisons must strip hyphens: str(uuid).replace("-","")

3. Missing received_at: NOT NULL constraint on completion_receipts.
   Raw INSERT was missing the column. Added received_at=now().

Full AC/CR cycle now verified:
  AC → 200, principal DID resolved from Keycloak token
  CR → 200, receipt ID + Chronicle CID returned
  Session → 200, chain of custody queryable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:51:55 -04:00
Tyler J King
4c58a4414b feat: JWT token extraction for Keycloak driver + deploy fix
Added _extract_token_data() to authorize endpoint. Extracts JWT
from Authorization header and passes it to the Keycloak identity
driver as _token_data. This was the missing link — the driver
needs the token to resolve the principal DID.

Verified on Hetzner:
  AC issued for tyler@bxnet.io →
    did:web:bxnet.capstone.guildhouse.dev/principal/tyler@bxnet.io
  Chronicle event emitted (GSAP_AC_ISSUED)

Known issue: CR endpoint has SQLAlchemy async greenlet bug
  (MissingGreenlet on the select+update in complete handler).
  AC issuance works. CR needs async session fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:24:18 -04:00
Tyler J King
0c77943ceb feat: governed function runtime + billing drain
GCAP-SPEC-FUNCTION-DESCRIPTOR-0001 implementation.
Mirrors connector runtime pattern exactly.

FunctionPlugin — trigger_events, handle(), descriptor(), knative_manifest()
FunctionRegistry — trigger_index for event-driven routing
FunctionRuntime — invoke() + dispatch() with Chronicle lineage
governed_function decorator — SDK surface for function authors
BillingProcessor — GSAP_CR_RECEIVED → billable event with Chronicle CID
EchoFunction — dev/test

API: /functions/ catalogue, invoke, dispatch, manifest, health
8 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 22:12:29 -04:00
Tyler J King
0987704264 feat: governed connector module
GCAP-SPEC-CONNECTOR-DESCRIPTOR-0001 implementation.

ConnectorPlugin — abstract base for governed connectors.
ConnectorRegistry — register/discover connectors.
ConnectorRuntime — wraps invoke with Chronicle lineage.
EchoConnector — dev/test example.

API endpoints:
  GET  /connectors/           — browse catalogue
  GET  /connectors/{id}/      — connector descriptor
  POST /connectors/{id}/invoke/ — governed invocation
  GET  /connectors/{id}/health/ — health check

5 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:42:38 -04:00
Tyler J King
cbc9ad73f7 feat: fastapi-gsap — lightweight GSAP broker PoC
Reference implementation of GCAP-SPEC-SHELLBOUND-BROKER-0001
in FastAPI. Designed for ISVs and enterprises implementing
the governed shell authorization protocol.

Architecture:
  FastAPI + SQLModel + Pydantic + async SQLite
  Single container deployment: Dockerfile included
  OpenAPI schema at /docs is the machine-readable GSAP contract

Broker Interface (§5):
  POST   /governance/authorize/     — Issue AC
  GET    /governance/authorize/{p}/ — Poll elevation
  POST   /governance/complete/      — Receive CR
  GET    /governance/session/{id}/  — View chain of custody
  POST   /governance/elevate/       — JIT elevation
  GET    /governance/drivers/       — List drivers

Identity Driver Interface (§2.2):
  IdentityDriver — abstract base (ISV extension point)
  KeycloakDriver — Keycloak implementation
  DriverRegistry — driver lookup and registration

Chronicle integration (§1.4):
  Optional CloudEvents emission via CHRONICLE_WEBHOOK_URL
  Forgejo push event format for receiver compatibility

Models:
  Pydantic schemas for AC, CR, Principal, Accord, Operation
  SQLModel DB models for persistence

Tests: 6 async tests including full AC→CR cycle

695 lines across 27 files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:10:21 -04:00
11ec21f311 Initial commit 2026-03-30 18:03:34 +00:00