feat(V1-API): implement TREX-Basic consumer API contract with encryption and persistence

- Add V1 API endpoints: assets, ledger, user-info
- Implement Fernet-based credential payload encryption (req-010)
- Extend health response schema with runtime info (req-004)
- Add service token runtime ID issuance (req-005)
- Implement database schema migration for V1 API (req-009)
- Add comprehensive test coverage for V1 endpoints
- Update SRS with 10 new requirements (REQ-004 through REQ-011)
- Create batch planning, design, and verification for BAT-001
- Integrate review-discovery StagePilot skill
- Update documentation indices and baseline specs
This commit is contained in:
2026-05-02 09:06:15 +00:00
parent 24096a1e58
commit ce63c75c9c
37 changed files with 2851 additions and 21 deletions
+17 -1
View File
@@ -7,7 +7,7 @@ from pathlib import Path
from urllib.parse import quote_plus
from dotenv import load_dotenv
from pydantic import Field, computed_field
from pydantic import Field, computed_field, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
# Load .env file from project root
@@ -50,6 +50,7 @@ class Settings(BaseSettings):
loki_host: str = Field(default="localhost", validation_alias="LOKI_HOST")
loki_port: int = Field(default=3100, validation_alias="LOKI_PORT")
loki_enabled: bool = Field(default=False, validation_alias="LOKI_ENABLED")
credential_encryption_key: str = Field(default="", validation_alias="CREDENTIAL_ENCRYPTION_KEY")
model_config = SettingsConfigDict(
env_prefix="PORTFOLIOHUB_",
@@ -58,6 +59,21 @@ class Settings(BaseSettings):
populate_by_name=True,
)
@model_validator(mode="after")
def _validate_credential_encryption_key(self) -> "Settings":
# Only validate at runtime when the key is actually used.
# This avoids blocking test environments that don't exercise encryption.
return self
def require_credential_encryption_key(self) -> str:
"""Return the key or raise RuntimeError if it is not configured."""
if not self.credential_encryption_key.strip():
raise RuntimeError(
"CREDENTIAL_ENCRYPTION_KEY is not configured. "
"Generate one with: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
)
return self.credential_encryption_key
@computed_field
@property
def allowed_origins(self) -> list[str]: