Add Foundation gateway roadmap and config profile
This commit is contained in:
parent
2355cf8114
commit
73629bf4f4
|
|
@ -0,0 +1,59 @@
|
|||
deployment_profile: "foundation_gateway"
|
||||
|
||||
server:
|
||||
host: "127.0.0.1"
|
||||
port: 8800
|
||||
|
||||
auth:
|
||||
# Keep a break-glass/static admin key only for initial provisioning or recovery.
|
||||
client_api_keys:
|
||||
- "change-me-foundation-admin-key"
|
||||
node_api_keys:
|
||||
- "change-me-node-key"
|
||||
enable_named_client_keys: true
|
||||
key_hash_secret_env: "GENIEHIVE_KEY_HASH_SECRET"
|
||||
|
||||
audit:
|
||||
enabled: true
|
||||
|
||||
admin_api:
|
||||
enabled: true
|
||||
|
||||
authorization:
|
||||
enforce_model_allowlists: true
|
||||
enforce_operation_allowlists: true
|
||||
empty_allowlist_means_no_access: true
|
||||
|
||||
storage:
|
||||
sqlite_path: "state/geniehive.foundation.sqlite3"
|
||||
|
||||
roles_path: "configs/roles.foundation.archive.yaml"
|
||||
|
||||
routing:
|
||||
health_stale_after_s: 30
|
||||
default_strategy: "scored"
|
||||
|
||||
providers:
|
||||
# Provider-backed services are optional. Keep API keys in environment variables,
|
||||
# not in YAML or client scripts.
|
||||
- provider_id: "openai-foundation"
|
||||
provider_kind: "openai_compatible"
|
||||
base_url: "https://api.openai.com"
|
||||
api_key_env: "OPENAI_API_KEY"
|
||||
enabled: false
|
||||
- provider_id: "anthropic-foundation"
|
||||
provider_kind: "anthropic_messages"
|
||||
base_url: "https://api.anthropic.com"
|
||||
api_key_env: "ANTHROPIC_API_KEY"
|
||||
default_headers:
|
||||
anthropic-version: "2023-06-01"
|
||||
enabled: false
|
||||
|
||||
budgeting:
|
||||
enabled: false
|
||||
reset_day_of_month: 1
|
||||
global_monthly_budget_cents: 5000
|
||||
provider_monthly_budget_cents:
|
||||
openai-foundation: 3000
|
||||
anthropic-foundation: 3000
|
||||
deny_on_unknown_cost: false
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# Foundation Gateway Baseline
|
||||
|
||||
Last updated: 2026-04-29
|
||||
|
||||
## Repository State
|
||||
|
||||
- Repository: `/home/netuser/bin/geniehive`
|
||||
- Baseline commit: `2355cf8114db5a1ac4630ca22aba63c703553f70`
|
||||
- Branch: `main`
|
||||
|
||||
## Current Capability Snapshot
|
||||
|
||||
GenieHive is currently a local-first control plane for heterogeneous generative
|
||||
AI services. It already supports:
|
||||
|
||||
- OpenAI-compatible `GET /v1/models`
|
||||
- OpenAI-compatible `POST /v1/chat/completions`
|
||||
- OpenAI-compatible `POST /v1/embeddings`
|
||||
- `POST /v1/audio/transcriptions` multipart proxying
|
||||
- node registration and heartbeat
|
||||
- SQLite-backed hosts, services, roles, and benchmark samples
|
||||
- role-based route resolution
|
||||
- request policy shaping
|
||||
- benchmark-informed route scoring
|
||||
- optional active service health probing
|
||||
- static client and node API keys
|
||||
|
||||
## Casual Deployment Behavior To Preserve
|
||||
|
||||
- `configs/control.example.yaml` loads without Foundation-specific sections.
|
||||
- Static `auth.client_api_keys` authorize client requests with `X-Api-Key`.
|
||||
- Static `auth.node_api_keys` authorize node requests with
|
||||
`X-GenieHive-Node-Key`.
|
||||
- Empty client or node key lists disable that auth check for development.
|
||||
- Local model servers do not require provider credential config.
|
||||
- Admin endpoints, audit logging, named keys, and budget checks are not required
|
||||
for a local-only deployment.
|
||||
|
||||
## Current Example Ports
|
||||
|
||||
- Control plane default: `127.0.0.1:8800`
|
||||
- Node examples commonly use localhost service endpoints for Ollama,
|
||||
llama.cpp, llamafile, or vLLM.
|
||||
- Recent ZeroTier test deployment used control plane binding
|
||||
`172.24.50.65:8800`, node `127.0.0.1:8891`, and llama.cpp
|
||||
`127.0.0.1:18091`.
|
||||
|
||||
## Baseline Verification
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
python -m pytest -q tests
|
||||
```
|
||||
|
||||
Expected current result at baseline: all tests pass.
|
||||
|
||||
Current verification result after adding the Foundation roadmap and config
|
||||
profile scaffold:
|
||||
|
||||
```text
|
||||
50 passed
|
||||
```
|
||||
|
||||
## Known Constraints
|
||||
|
||||
- Client authentication is static-key based, not named or revocable per user.
|
||||
- Request attribution is not currently persisted.
|
||||
- Provider credentials are not modeled as first-class control-plane objects.
|
||||
- No budget or quota enforcement exists.
|
||||
- Anthropic Messages API is not natively adapted behind the OpenAI-compatible
|
||||
facade.
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
# Foundation Gateway Roadmap
|
||||
|
||||
Last updated: 2026-04-29
|
||||
|
||||
## Decision
|
||||
|
||||
Do not fork GenieHive for the Foundation AI gateway work. Implement the feature
|
||||
set as an optional hardening profile on top of the existing local-first control
|
||||
plane.
|
||||
|
||||
The core project should continue to support casual deployment:
|
||||
|
||||
- local model services remain first-class
|
||||
- static `client_api_keys` and `node_api_keys` remain supported
|
||||
- empty key lists can still disable auth for development
|
||||
- audit logging, named keys, quotas, provider accounts, and admin endpoints are
|
||||
opt-in
|
||||
|
||||
Foundation deployments should enable stricter controls through config, role
|
||||
catalogs, and operator documentation.
|
||||
|
||||
## Design Principle
|
||||
|
||||
Separate mechanism from policy.
|
||||
|
||||
Core GenieHive mechanisms:
|
||||
|
||||
- authenticate a client and attach a request identity
|
||||
- route OpenAI-compatible requests through roles and services
|
||||
- optionally record audit metadata without prompt or completion content
|
||||
- optionally enforce model and operation scopes
|
||||
- optionally route to external provider-backed services
|
||||
- optionally summarize usage and enforce budgets
|
||||
|
||||
Foundation policy:
|
||||
|
||||
- who may receive a key
|
||||
- what models and roles are approved
|
||||
- what budgets apply
|
||||
- what provider accounts are used
|
||||
- how requests are reviewed before public publication
|
||||
- how emergency disable and key rotation are performed
|
||||
|
||||
## Compatibility Contract
|
||||
|
||||
Every Foundation hardening change must preserve these behaviors unless a config
|
||||
explicitly opts into stricter operation:
|
||||
|
||||
1. Existing `configs/control.example.yaml` continues to load.
|
||||
2. Existing static `auth.client_api_keys` continues to authorize requests.
|
||||
3. Existing node registration keys continue to work.
|
||||
4. Existing role catalogs continue to route without client allowlists.
|
||||
5. `GET /v1/models`, chat, embeddings, transcription, and cluster inspection
|
||||
remain available in casual deployments.
|
||||
6. No provider credentials are required for local-only deployment.
|
||||
7. Admin endpoints are disabled unless admin authentication is configured.
|
||||
|
||||
## Profiles
|
||||
|
||||
### Casual Profile
|
||||
|
||||
The casual profile is the default shape of GenieHive.
|
||||
|
||||
Expected traits:
|
||||
|
||||
- local or LAN-bound control plane
|
||||
- static shared client key, or no auth during isolated development
|
||||
- no audit log by default
|
||||
- no budget enforcement
|
||||
- no provider credential store
|
||||
- no admin API exposed by default
|
||||
|
||||
### Foundation Gateway Profile
|
||||
|
||||
The Foundation gateway profile is an opt-in deployment mode for managed access
|
||||
to local and paid AI services.
|
||||
|
||||
Expected traits:
|
||||
|
||||
- named, revocable client credentials
|
||||
- request audit log without prompt or completion content
|
||||
- model and operation allowlists per key
|
||||
- Foundation-owned provider account indirection
|
||||
- optional budget and quota enforcement
|
||||
- migration-specific role catalogs
|
||||
- operator and board-readable governance documentation
|
||||
|
||||
## Configuration Shape
|
||||
|
||||
The final config shape may evolve, but the intended compatibility model is:
|
||||
|
||||
```yaml
|
||||
deployment_profile: "casual"
|
||||
|
||||
auth:
|
||||
client_api_keys:
|
||||
- "change-me-client-key"
|
||||
node_api_keys:
|
||||
- "change-me-node-key"
|
||||
enable_named_client_keys: false
|
||||
key_hash_secret_env: "GENIEHIVE_KEY_HASH_SECRET"
|
||||
|
||||
audit:
|
||||
enabled: false
|
||||
|
||||
admin_api:
|
||||
enabled: false
|
||||
|
||||
authorization:
|
||||
enforce_model_allowlists: false
|
||||
enforce_operation_allowlists: false
|
||||
empty_allowlist_means_no_access: true
|
||||
|
||||
providers: []
|
||||
|
||||
budgeting:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Foundation example configs can switch these flags on. Casual example configs
|
||||
should stay short and understandable.
|
||||
|
||||
## Revised Milestones
|
||||
|
||||
### M0: Baseline and Compatibility Guard
|
||||
|
||||
Goal: record the current behavior and make compatibility explicit before adding
|
||||
governance features.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add `docs/foundation_gateway_baseline.md`.
|
||||
- Record current commit, test command, existing exposed ports, and supported
|
||||
casual deployment behavior.
|
||||
- Add or preserve tests proving `configs/control.example.yaml` still loads and
|
||||
static `X-Api-Key` auth still works.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Baseline document exists.
|
||||
- Current test suite passes or failures are documented.
|
||||
- Compatibility contract is visible in docs.
|
||||
|
||||
### M1: Config Profiles and Feature Flags
|
||||
|
||||
Goal: introduce opt-in switches without changing runtime behavior.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add config models for `deployment_profile`, `audit`, `admin_api`,
|
||||
`authorization`, `providers`, and `budgeting`.
|
||||
- Keep default values equivalent to current casual behavior.
|
||||
- Add a Foundation example config skeleton.
|
||||
- Add tests for default values and legacy config loading.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Existing configs load unchanged.
|
||||
- New config sections are accepted.
|
||||
- No governance feature activates by default.
|
||||
|
||||
### M2: Named Client Credentials
|
||||
|
||||
Goal: support named, revocable API keys while keeping static keys working.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add `ClientContext` with principal metadata.
|
||||
- Add API key generation, hashing, verification, and redaction helpers.
|
||||
- Add a `client_keys` SQLite table.
|
||||
- Add registry methods to create, list, disable, enable, and touch keys.
|
||||
- Support named keys only when `auth.enable_named_client_keys` is true.
|
||||
- Preserve static `auth.client_api_keys`.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Static keys still work.
|
||||
- Named keys work through `X-Api-Key` when enabled.
|
||||
- Disabled named keys fail.
|
||||
- Raw keys are never stored.
|
||||
- Request handlers can read authenticated client context.
|
||||
|
||||
### M3: Request Audit Log
|
||||
|
||||
Goal: make production requests attributable without storing prompt or completion
|
||||
content.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add request ID generation from `X-Request-Id` or UUID.
|
||||
- Add `request_audit_log` SQLite table.
|
||||
- Record identity, operation, requested model, resolved service, upstream model,
|
||||
provider kind, status, duration, token usage when available, estimated cost
|
||||
when available, and error category.
|
||||
- Add admin-only query and summary endpoints, disabled unless admin API is
|
||||
enabled.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Chat, embeddings, and transcription requests create audit rows when enabled.
|
||||
- Prompt and completion content are not logged.
|
||||
- Failed routing and upstream errors are logged.
|
||||
- Casual deployments have no audit behavior unless enabled.
|
||||
|
||||
### M4: Model and Operation Authorization
|
||||
|
||||
Goal: let Foundation keys be limited to approved roles, models, and operations.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add allowed models and allowed operations to named keys.
|
||||
- Enforce operation scopes only when authorization enforcement is enabled.
|
||||
- Support exact model IDs and conservative glob patterns such as `local/*`,
|
||||
`openai/*`, `anthropic/*`, and `role/*`.
|
||||
- Prefer role IDs for migration workflows.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- A chat-only key cannot call embeddings when enforcement is enabled.
|
||||
- A key restricted to `archive_migrator` cannot call unrelated roles.
|
||||
- Legacy static keys are unaffected unless explicitly mapped into stricter mode.
|
||||
|
||||
### M5: Archive Migration Profile
|
||||
|
||||
Goal: support TalkOrigins/SciSiteForge-style migration without direct provider
|
||||
keys in migration scripts.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add `configs/roles.foundation.archive.yaml`.
|
||||
- Add roles such as `archive_migrator`, `archive_metadata_extractor`,
|
||||
`archive_link_reviewer`, `archive_copyeditor`, and
|
||||
`archive_factcheck_assistant`.
|
||||
- Add `configs/control.foundation.example.yaml`.
|
||||
- Add `configs/clients/archive_migration.example.env`.
|
||||
- Add a smoke script that calls `archive_migrator` through the OpenAI-compatible
|
||||
facade.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- A migration client only needs `GENIEHIVE_BASE_URL`, `GENIEHIVE_API_KEY`, and
|
||||
`GENIEHIVE_MODEL`.
|
||||
- The requested model is a role, not a provider-specific model.
|
||||
- Local-only provider routing remains possible.
|
||||
|
||||
### M6: Provider Credential Indirection
|
||||
|
||||
Goal: keep paid provider credentials out of role configs, node configs, and
|
||||
client scripts.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add provider config entries using environment variables first.
|
||||
- Add external/provider-backed service registration without requiring node
|
||||
heartbeat.
|
||||
- Resolve provider headers centrally in the upstream layer.
|
||||
- Keep provider credential storage optional; encrypted-at-rest credentials can
|
||||
be deferred.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Provider keys are loaded from environment variables, not committed YAML.
|
||||
- Provider-backed services can be routed like local services.
|
||||
- Local-only deployments do not need provider sections.
|
||||
|
||||
### M7: Anthropic Messages Adapter
|
||||
|
||||
Goal: expose Anthropic models through the existing OpenAI-compatible chat facade.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add provider protocol dispatch in `UpstreamClient`.
|
||||
- Transform OpenAI-shaped messages into Anthropic Messages requests.
|
||||
- Transform Anthropic responses back to OpenAI-compatible chat completions.
|
||||
- Reject Anthropic streaming clearly until implemented.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- A chat request can route to an Anthropic-backed service.
|
||||
- System messages and usage fields are mapped correctly.
|
||||
- Unsupported streaming fails with a specific error.
|
||||
|
||||
### M8: Budget and Quota Enforcement
|
||||
|
||||
Goal: prevent accidental provider overspend.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add budget config with disabled default.
|
||||
- Use audit summaries to calculate monthly usage.
|
||||
- Add request, token, and estimated-cost limits per key, provider, and globally.
|
||||
- Add configurable price maps.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Requests over configured limits are denied before upstream calls.
|
||||
- Unknown-cost behavior is configurable.
|
||||
- Casual deployments do not perform budget checks.
|
||||
|
||||
### M9: Admin CLI and Operations Docs
|
||||
|
||||
Goal: make managed operation scriptable and understandable.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add `geniehive-admin` CLI for create/list/disable/enable keys and usage
|
||||
summaries.
|
||||
- Add Foundation docs for gateway operation, provider accounts, key management,
|
||||
archive migration workflow, and emergency disable.
|
||||
- Document when provider-native seats are needed instead of GenieHive routing.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- A new operator can provision and revoke a user key without editing SQLite.
|
||||
- A board-facing control summary explains ownership, auditability, and budget
|
||||
control.
|
||||
|
||||
### M10: Security Review
|
||||
|
||||
Goal: make the Foundation profile safe to expose beyond localhost.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add a security checklist covering provider keys, admin auth, content logging,
|
||||
CORS, TLS/reverse proxy, backup/restore, rate limits, and emergency disable.
|
||||
- Implement critical checklist items or explicitly defer with issue references.
|
||||
- Keep WAN and zero-trust networking as deployment concerns unless a concrete
|
||||
need appears.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Security checklist exists.
|
||||
- Critical production risks have implementation or documented mitigations.
|
||||
|
||||
## Initial Implementation Order
|
||||
|
||||
1. M0: Baseline and compatibility guard.
|
||||
2. M1: Config profiles and feature flags.
|
||||
3. M2: Named client credentials.
|
||||
4. M3: Request audit log.
|
||||
5. M4: Model and operation authorization.
|
||||
6. M5: Archive migration profile.
|
||||
7. M6: Provider credential indirection.
|
||||
8. M7: Anthropic Messages adapter.
|
||||
9. M8: Budget and quota enforcement.
|
||||
10. M9: Admin CLI and operations docs.
|
||||
11. M10: Security review.
|
||||
|
||||
This order lets local-only and TalkOrigins migration pilots start before paid
|
||||
provider routing and budget controls are complete.
|
||||
|
|
@ -14,6 +14,39 @@ class ServerConfig(BaseModel):
|
|||
class AuthConfig(BaseModel):
|
||||
client_api_keys: list[str] = Field(default_factory=list)
|
||||
node_api_keys: list[str] = Field(default_factory=list)
|
||||
enable_named_client_keys: bool = False
|
||||
key_hash_secret_env: str = "GENIEHIVE_KEY_HASH_SECRET"
|
||||
|
||||
|
||||
class AuditConfig(BaseModel):
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
class AdminApiConfig(BaseModel):
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
class AuthorizationConfig(BaseModel):
|
||||
enforce_model_allowlists: bool = False
|
||||
enforce_operation_allowlists: bool = False
|
||||
empty_allowlist_means_no_access: bool = True
|
||||
|
||||
|
||||
class ProviderConfig(BaseModel):
|
||||
provider_id: str
|
||||
provider_kind: str
|
||||
base_url: str
|
||||
api_key_env: str | None = None
|
||||
default_headers: dict[str, str] = Field(default_factory=dict)
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class BudgetingConfig(BaseModel):
|
||||
enabled: bool = False
|
||||
reset_day_of_month: int = 1
|
||||
global_monthly_budget_cents: int | None = None
|
||||
provider_monthly_budget_cents: dict[str, int] = Field(default_factory=dict)
|
||||
deny_on_unknown_cost: bool = False
|
||||
|
||||
|
||||
class StorageConfig(BaseModel):
|
||||
|
|
@ -33,8 +66,14 @@ class RoutingConfig(BaseModel):
|
|||
|
||||
|
||||
class ControlConfig(BaseModel):
|
||||
deployment_profile: str = "casual"
|
||||
server: ServerConfig = Field(default_factory=ServerConfig)
|
||||
auth: AuthConfig = Field(default_factory=AuthConfig)
|
||||
audit: AuditConfig = Field(default_factory=AuditConfig)
|
||||
admin_api: AdminApiConfig = Field(default_factory=AdminApiConfig)
|
||||
authorization: AuthorizationConfig = Field(default_factory=AuthorizationConfig)
|
||||
providers: list[ProviderConfig] = Field(default_factory=list)
|
||||
budgeting: BudgetingConfig = Field(default_factory=BudgetingConfig)
|
||||
storage: StorageConfig = Field(default_factory=StorageConfig)
|
||||
routing: RoutingConfig = Field(default_factory=RoutingConfig)
|
||||
roles_path: str | None = None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
from pathlib import Path
|
||||
|
||||
from geniehive_control.config import ControlConfig, load_config
|
||||
|
||||
|
||||
def test_default_control_config_is_casual_and_non_governed() -> None:
|
||||
cfg = ControlConfig()
|
||||
|
||||
assert cfg.deployment_profile == "casual"
|
||||
assert cfg.auth.client_api_keys == []
|
||||
assert cfg.auth.node_api_keys == []
|
||||
assert cfg.auth.enable_named_client_keys is False
|
||||
assert cfg.audit.enabled is False
|
||||
assert cfg.admin_api.enabled is False
|
||||
assert cfg.authorization.enforce_model_allowlists is False
|
||||
assert cfg.authorization.enforce_operation_allowlists is False
|
||||
assert cfg.providers == []
|
||||
assert cfg.budgeting.enabled is False
|
||||
|
||||
|
||||
def test_legacy_control_example_loads_without_foundation_sections() -> None:
|
||||
cfg = load_config(Path("configs/control.example.yaml"))
|
||||
|
||||
assert cfg.deployment_profile == "casual"
|
||||
assert cfg.auth.client_api_keys == ["change-me-client-key"]
|
||||
assert cfg.auth.node_api_keys == ["change-me-node-key"]
|
||||
assert cfg.auth.enable_named_client_keys is False
|
||||
assert cfg.audit.enabled is False
|
||||
assert cfg.admin_api.enabled is False
|
||||
assert cfg.providers == []
|
||||
|
||||
|
||||
def test_foundation_control_example_loads_as_opt_in_profile() -> None:
|
||||
cfg = load_config(Path("configs/control.foundation.example.yaml"))
|
||||
|
||||
assert cfg.deployment_profile == "foundation_gateway"
|
||||
assert cfg.auth.enable_named_client_keys is True
|
||||
assert cfg.audit.enabled is True
|
||||
assert cfg.admin_api.enabled is True
|
||||
assert cfg.authorization.enforce_model_allowlists is True
|
||||
assert cfg.authorization.enforce_operation_allowlists is True
|
||||
assert cfg.providers[0].provider_id == "openai-foundation"
|
||||
assert cfg.providers[0].api_key_env == "OPENAI_API_KEY"
|
||||
assert cfg.providers[1].provider_kind == "anthropic_messages"
|
||||
assert cfg.budgeting.global_monthly_budget_cents == 5000
|
||||
Loading…
Reference in New Issue