Skip to content

feat: registration domain refactor + Bedrock & Vertex runtime adapters #14

@iliassjabali

Description

@iliassjabali

Context

Issue #13 implements agentspec register as a thin CLI wrapper around POST /api/v1/register. That issue deliberately scopes out architectural changes to the control plane. This issue addresses the next layer: refactoring the registration flow so it can validate against real runtimes (Bedrock, Vertex, K8s) before issuing a token.

Prerequisite: #13 must be merged first.


Problem

packages/control-plane/api/register.py is a single FastAPI route handler that mixes HTTP request parsing, DB access, and token issuance in one place. There is no hook invoked on registration, so:

  1. AgentObservation CRs are never created at register time — only on the first heartbeat
  2. There is no path to validate an agent against AWS Bedrock or GCP Vertex before issuing a token
  3. The registration logic cannot be reused from the CLI without going through HTTP

Proposed Solution

Split the registration flow into a domain core that contains all business logic, and ports (interfaces) that the domain depends on for storage, token issuance, and runtime notification. Concrete adapters implement those ports for each target.

      flowchart LR
    CLI((CLI)) --> Domain
    HTTP((HTTP)) --> Domain

    subgraph Domain ["Registration Domain"]
        direction TB
        Main["register_agent(name, runtime, manifest)"]
        Steps["• validate name (RFC 1123)<br>• invoke RuntimeNotifierPort<br>• store agent record<br>• issue JWT"]
        Main --- Steps
    end

    Domain --> ASP[AgentStorePort]
    Domain --> TIP[TokenIssuerPort]
    Domain --> RNP[RuntimeNotifierPort]
    
    classDef domainBox fill:#f9f9f9,stroke:#333,stroke-width:2px;
    class Domain domainBox;
Loading

Ports (Python ABCs)

Port Methods
AgentStorePort save(agent), find_by_name(name)
TokenIssuerPort issue(agent_id) → (token, jti)
RuntimeNotifierPort on_register(agent) → None

Adapters

Adapter Runtime Behavior
PostgresStoreAdapter all SQLAlchemy async upsert — wraps existing db/models.py
JwtIssuerAdapter all Wraps existing auth/keys.py — no change to token format
K8sNotifierAdapter k8s Calls upsert_agent_observation() from k8s/upsert.py at register time, not just on heartbeat
BedrockNotifierAdapter bedrock Calls AWS Bedrock Agents API to validate agent exists; raises RuntimeValidationError if not found
VertexNotifierAdapter vertex Calls GCP Vertex AI Agents API to validate agent exists; raises RuntimeValidationError if not found
NoopNotifierAdapter local, docker Pass-through — no external validation

Note on bedrock and vertex runtimes

These extend the runtime enum in RegisterRequest (currently docker | local | k8s). Validation is registration-only — heartbeat protocol, CR structure, and operator reconciliation are unchanged.


Acceptance Criteria

Domain

  • register_agent() in domain/registration.py has zero HTTP, DB, or K8s imports
  • All three ports are Python ABCs in ports/
  • Domain unit-tested with in-memory mock adapters — no real DB or HTTP
  • Key rotation (idempotent re-registration) preserved in domain logic

Adapters

  • K8sNotifierAdapter — CR created at register time, not only on heartbeat
  • BedrockNotifierAdapter — strict: RuntimeValidationError(400) if agent not found
  • VertexNotifierAdapter — strict: RuntimeValidationError(400) if agent not found
  • NoopNotifierAdapter — no side effects for local and docker

HTTP layer

  • api/register.py becomes a thin adapter: parse → call register_agent() → return response
  • Existing POST /api/v1/register behavior unchanged (same response schema, idempotent rotation)
  • All existing control-plane tests pass (pytest tests/ -v)

CLI integration (depends on #13)

  • agentspec register agent.yaml --runtime bedrock triggers Bedrock validation before token issuance
  • agentspec register agent.yaml --runtime vertex triggers Vertex validation before token issuance
  • Clear error on failure: ✗ Runtime validation failed: Bedrock agent 'my-agent' not found in account

Files Changed

File Action
packages/control-plane/domain/registration.py Create — domain core, no I/O imports
packages/control-plane/ports/agent_store.py CreateAgentStorePort ABC
packages/control-plane/ports/token_issuer.py CreateTokenIssuerPort ABC
packages/control-plane/ports/runtime_notifier.py CreateRuntimeNotifierPort ABC + RuntimeValidationError
packages/control-plane/adapters/postgres_store.py Create — wraps db/models.py
packages/control-plane/adapters/jwt_issuer.py Create — wraps auth/keys.py
packages/control-plane/adapters/k8s_notifier.py Create — wraps k8s/upsert.py
packages/control-plane/adapters/bedrock_notifier.py Create
packages/control-plane/adapters/vertex_notifier.py Create
packages/control-plane/adapters/noop_notifier.py Create
packages/control-plane/api/register.py Refactor — thin HTTP adapter
packages/control-plane/schemas.py Edit — extend runtime enum with bedrock, vertex
packages/control-plane/tests/test_registration_domain.py Create — unit tests with mock adapters

Unchanged: k8s/upsert.py, auth/keys.py, db/models.py, db/base.py


Out of Scope

  • Python SDK start_push_mode() changes
  • Operator Helm chart changes
  • JWT format changes
  • K8s CR schema changes
  • Bedrock/Vertex heartbeat handling

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions