Files
poimen-memory/docs/DEPLOYMENT_CHECKLIST.md
T
Story Crater Bot 277d719278 feat: Memory Service API ready for deployment — Vault JSON endpoints + Hybrid search
API Changes (crates/mem-cli/src/http_server.rs):

 Vault Endpoints (JSON API):
  - GET /memory/vault → {projects: [...]}
  - GET /memory/vault?project=X → {project: X, files: [...]}
  - GET /memory/vault/{proj}/{file} → {metadata: {...}, content: '...'}
  - YAML frontmatter parsed to JSON metadata
  - Auth: JWT on all endpoints

 Search Endpoints:
  - GET /memory/query?method=semantic → pgvector only (60% weight)
  - GET /memory/query?method=hybrid (default) → pgvector + OpenSearch (fallback to semantic)
  - Hybrid score: 0.6*semantic + 0.4*lexical
  - Limit: top-10 results (default)

 AppState Extended:
  - opensearch_client: Option<Arc<OpenSearchClient>>
  - Initialized from OPENSEARCH_HOSTS env var (optional)
  - Graceful fallback if OpenSearch unavailable

 Handlers Updated:
  - vault_browser_handler() → returns JSON projects list
  - vault_project_tree() → helper for file tree generation
  - vault_project_handler() → GET /{project} → file tree JSON
  - vault_file_handler() → GET /{project}/{file} → JSON with metadata + content
  - query_handler() → hybrid search with semantic fallback

K8s Manifests (k8s/infra/databases/opensearch.yaml):

 OpenSearch StatefulSet:
  - 2 replicas for HA cluster (opensearch-0, opensearch-1)
  - Image: opensearchproject/opensearch:2.11.0
  - Services: opensearch (headless), opensearch-internal (ClusterIP 9200)
  - ConfigMap: opensearch.yml with cluster settings
  - PVC: 30Gi per pod (Longhorn storage class)
  - ServiceAccount + NetworkPolicy (Memory Service only)
  - Init container: set vm.max_map_count=262144
  - Probes: liveness (60s), readiness (30s)
  - Resources: 512Mi-1Gi memory, 250m-500m CPU
  - Security: plugins.security.disabled (K8s network isolated)

 Updated kustomization.yaml:
  - Added opensearch.yaml to resources

Documentation:

 docs/API_VAULT_ENDPOINTS.md (10KB):
  - Complete API reference with examples
  - Architecture: semantic (pgvector IVFFlat) + lexical (OpenSearch BM25)
  - Fusion strategy: weighted linear combination (60/40 split)
  - DNS records for vault.riotpiao.com + memory.riotpiao.com
  - Ingress configuration (dual-domain routing)
  - Frontend integration examples (React/Vue)
  - Fallback behavior (graceful degradation)
  - Performance tuning (IVFFlat lists, OpenSearch shards)
  - Security: JWT validation, rate limiting, field-level ACL (future)

 docs/DEPLOYMENT_CHECKLIST.md (8KB):
  - 5-phase deployment plan (API ready, OpenSearch, DNS, Testing, Frontend)
  - Step-by-step deployment commands
  - Testing procedures for vault + search endpoints
  - Troubleshooting: OpenSearch not found, cluster red, JWT validation
  - Monitoring metrics + dashboard queries
  - Fallback scenarios + error codes

Environment Variables:

- OPENSEARCH_HOSTS (optional, e.g., "opensearch-internal.poimen.svc.cluster.local:9200")
  - If unset: hybrid search disabled, falls back to semantic
  - CSV list supported: "host1:9200,host2:9200"

Deployment Summary:

1.  API code ready (JSON endpoints, fallback to semantic if OpenSearch unavailable)
2.  OpenSearch K8s manifests (StatefulSet + networking)
3.  Documentation (API reference + deployment guide)
4.  Ready to: kubectl apply -k k8s/infra/databases/

Backward Compatibility:

 Existing JSON endpoints work without change
⚠️ HTML endpoints replaced with JSON (breaking change for old clients)
 Graceful fallback: hybrid search → semantic if OpenSearch missing
 Rate limiting preserved on all endpoints

Testing Ready:

- Vault tree endpoint testable after deployment
- Hybrid search testable once OpenSearch cluster ready
- All endpoints require JWT from Authentik
- Load test script provided

Next: Deploy OpenSearch + test against vault.riotpiao.com
2026-08-27 21:05:09 -07:00

7.8 KiB

Deployment Checklist: Memory Service API Ready

Phase 1: API Endpoints Ready

Vault Endpoints (JSON API)

  • GET /memory/vault — list projects
  • GET /memory/vault?project=X — file tree
  • GET /memory/vault/{project}/{file} — file content (JSON)
  • YAML frontmatter parsing to JSON metadata
  • JWT auth on all endpoints

Search Endpoints

  • GET /memory/query?method=semantic — pgvector only
  • GET /memory/query?method=hybrid — semantic + OpenSearch (with fallback)
  • Hybrid score fusion (60% semantic + 40% lexical)
  • JWT auth required

Code Changes

  • crates/mem-cli/src/http_server.rs — vault + search handlers
    • vault_browser_handler() — returns JSON projects list
    • vault_project_tree() — helper for file tree
    • vault_project_handler() — GET /{project} → file tree
    • vault_file_handler() — GET /{project}/{file} → JSON content
    • query_handler() — updated for hybrid search with OpenSearch fallback
    • AppState.opensearch_client — optional OpenSearch integration
  • Environment variable: OPENSEARCH_HOSTS (optional)

Phase 2: OpenSearch Deployment

K8s Manifests

  • k8s/infra/databases/opensearch.yaml

    • StatefulSet: 2 replicas (opensearch-0, opensearch-1)
    • Service: opensearch (headless), opensearch-internal (ClusterIP)
    • ConfigMap: opensearch.yml configuration
    • PVC: 30Gi per pod (Longhorn)
    • ServiceAccount + NetworkPolicy
    • Probes: liveness (60s), readiness (30s)
    • Security: security plugin disabled (assume K8s network isolation)
  • Updated k8s/infra/databases/kustomization.yaml

    • Added - opensearch.yaml to resources

Deployment Steps

# 1. Deploy OpenSearch
kubectl apply -k k8s/infra/databases/

# 2. Wait for StatefulSet ready
kubectl get pods -n poimen -l app.kubernetes.io/name=opensearch -w

# Expected:
# opensearch-0   1/1 Running
# opensearch-1   1/1 Running

# 3. Verify cluster health
kubectl port-forward -n poimen svc/opensearch-internal 9200:9200 &
curl http://localhost:9200/_cluster/health
# {"status":"green","...}

# 4. Configure Memory Service
kubectl set env deployment poimen-memory \
  -n poimen \
  OPENSEARCH_HOSTS=opensearch-internal.poimen.svc.cluster.local:9200

# 5. Restart Memory Service
kubectl rollout restart deployment poimen-memory -n poimen

Phase 3: DNS & Ingress

DNS Records

vault.riotpiao.com     IN A <cluster-ip>
memory.riotpiao.com    IN A <cluster-ip>

Ingress Routes

vault.riotpiao.com → /memory/vault/* endpoints (vault browser) memory.riotpiao.com → full API (search, projects, skills, etc.)

Sample Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: memory-ingress
  namespace: poimen
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - vault.riotpiao.com
        - memory.riotpiao.com
      secretName: memory-tls
  rules:
    - host: vault.riotpiao.com
      http:
        paths:
          - path: /memory/vault
            pathType: Prefix
            backend:
              service:
                name: poimen-memory
                port: {number: 8080}
    - host: memory.riotpiao.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: poimen-memory
                port: {number: 8080}

Phase 4: Testing

Test Vault Endpoints

# Authenticate
TOKEN=$(curl -X POST https://authentik.riotpiao.com/application/o/token/ \
  -d "client_id=poimen-memory" \
  -d "client_secret=..." \
  -d "grant_type=client_credentials" | jq -r .access_token)

# List projects
curl -H "Authorization: Bearer $TOKEN" \
  https://vault.riotpiao.com/memory/vault
# Expected: {"projects": ["poimen", ...]}

# List files in project
curl -H "Authorization: Bearer $TOKEN" \
  'https://vault.riotpiao.com/memory/vault?project=poimen'
# Expected: {"project": "poimen", "files": [...]}

# Get file content
curl -H "Authorization: Bearer $TOKEN" \
  https://vault.riotpiao.com/memory/vault/poimen/index
# Expected: {"project": "poimen", "file": "index.md", "metadata": {...}, "content": "..."}

Test Search Endpoints

# Semantic only
curl -H "Authorization: Bearer $TOKEN" \
  'https://memory.riotpiao.com/memory/query?project=poimen&query=kubernetes&method=semantic'
# Expected: {"method": "semantic", "results": [...]}

# Hybrid (best)
curl -H "Authorization: Bearer $TOKEN" \
  'https://memory.riotpiao.com/memory/query?project=poimen&query=kubernetes'
# Expected: {"method": "hybrid", "results": [...]}
# (or "semantic_fallback" if OpenSearch not ready)

Load Test

ab -n 1000 -c 10 \
  -H "Authorization: Bearer $TOKEN" \
  'https://memory.riotpiao.com/memory/query?project=poimen&query=test'

Phase 5: Frontend Deployment (Next)

Waiting on:

  • React SPA build
  • Vault browser UI
  • Search form + result display
  • GRC workflow (edit → MR → merge)
  • Agent execution tracking

Monitoring

Endpoints Health

# Memory Service
curl https://memory.riotpiao.com/health

# OpenSearch
curl https://vault.riotpiao.com/memory/query?project=test&query=test
# If errors → check OPENSEARCH_HOSTS config

# Metrics
kubectl logs -n poimen -l app.kubernetes.io/name=poimen-memory --tail=100

Dashboard Metrics

# Query latency
histogram_quantile(0.95, http_request_duration_seconds{method="GET", endpoint="/memory/query"})

# Cache hit rate
opensearch_query_cache_hit_count / opensearch_query_cache_total

# Cluster health
opensearch_cluster_health_status  # 1 = green, 0 = red

Fallback Behavior

If OpenSearch is Down

/memory/vault/* endpoints work (no OpenSearch dependency) /memory/query with method=semantic works ⚠️ /memory/query with method=hybrid falls back to semantic (no error) /memory/query with method=hybrid&strict=true returns 503

If pgvector is Down

All endpoints fail (core dependency)

If Authentik is Down

All endpoints fail with 401 (no auth)


Troubleshooting

OpenSearch not found

Symptom: "semantic_fallback" always returned, no hybrid scores

Fix:

# Check env var
kubectl get deployment -n poimen poimen-memory -o yaml | grep OPENSEARCH_HOSTS

# Set it
kubectl set env deployment poimen-memory -n poimen \
  OPENSEARCH_HOSTS=opensearch-internal.poimen.svc.cluster.local:9200
kubectl rollout restart deployment poimen-memory -n poimen

# Verify connectivity from pod
kubectl exec -n poimen <pod> -- curl http://opensearch-internal:9200/_cluster/health

OpenSearch cluster red

Symptom: opensearch-1 not starting, cluster unhealthy

Fix:

# Check logs
kubectl logs -n poimen opensearch-1

# Common issues:
# 1. vm.max_map_count too low (init container should fix)
# 2. PVC not provisioned (check Longhorn)
# 3. Memory limit too low (increase to 1Gi)

# Reset cluster
kubectl delete pvc opensearch-data-opensearch-1 -n poimen
kubectl delete pod opensearch-1 -n poimen

JWT validation fails on queries

Symptom: 401 Unauthorized: JWT validation failed

Fix:

# Check Authentik JWKS accessible
curl https://authentik.riotpiao.com/application/o/poimen-memory/jwks/

# Check token not expired
jwt decode <your-token>  # look at 'exp' claim

# Check token has required claims
# Should have: iss, aud, sub, roles, permissions

Checklist Summary

Ready for Production

  • API endpoints return JSON (not HTML)
  • Vault tree endpoint working
  • Hybrid search implemented (with fallback)
  • OpenSearch manifests created
  • JWT auth on all endpoints
  • Environment variables documented
  • Deployment guide written
  • Frontend React app deployed
  • GRC workflow endpoints implemented
  • Load tested at scale
  • Monitoring configured

Status: Ready to deploy OpenSearch + test API