Skip to main content

Overview

The web-api is the primary API backend for the Rapida dashboard. Every request from the browser — authentication, organization setup, assistant management, credential storage — goes through this service. It also acts as the gRPC proxy for downstream services, validating JWT tokens before forwarding requests.

Port

9001 — HTTP · gRPC · gRPC-web (cmux)

Language

Go 1.25 Gin (REST) + gRPC

Storage

PostgreSQL web_db Redis (session cache)
All gRPC-web requests from the browser, except for real-time audio, are routed through web-api. The service validates the JWT and proxies the request to the correct downstream service using typed gRPC clients from pkg/clients/.

Components

Handles user registration, login, password recovery, OAuth 2.0 flows, and JWT issuance.
FeatureDetail
Token typeJWT (signed with SECRET, shared across all services)
Token storageClient-side (Authorization header)
Session cacheRedis DB 1
OAuth providersGoogle, GitHub, Microsoft (configured per deployment)
Token expiryConfigurable; default 24 hours
Every resource in Rapida is scoped to an Organization → Project hierarchy. The web-api enforces this scoping at the gRPC interceptor level.
Organization
└── Project
    ├── Assistants
    ├── Knowledge Bases
    ├── Integrations
    └── Webhooks
Each entity stores organization_id and project_id via the Organizational base model. The gRPC auth interceptor rejects any request where the JWT’s organization claim does not match the target resource.
Provider API keys and OAuth tokens are encrypted with AES-256 before storage. The encryption key is derived from SECRET. The vault is the source of truth for all provider credentials — integration-api reads from it at call time.
OperationBehavior
Store keyAES-256-GCM encrypt → write to web_db
Retrieve keyRead from web_db → decrypt in-memory → forward to integration-api
Rotate keyReplace ciphertext; existing calls in flight are unaffected
AuditEvery vault read/write is logged with user ID and timestamp
The web-api acts as a proxy for all dashboard gRPC calls. It validates the JWT, extracts the organization context, and forwards to the correct downstream service.
gRPC Path PrefixForwarded To
/web_apiLocal (web-api owns this)
/vault_apiLocal (web-api owns this)
/workflow_api · /assistant_apiassistant-api:9007
/knowledge_apiassistant-api:9007
/tool_api · /endpoint_api · /webhook_apiendpoint-api:9005
/provider_api · /integration_apiintegration-api:9004
/connect_apiLocal (OAuth connector)
/document_apidocument-api:9010
/lead_apiLocal
All entities compose base GORM models:
Base ModelFields
Auditedid (Snowflake), created_at, updated_at
Mutablestatus, created_by, updated_by
Organizationalproject_id, organization_id
IDs are generated as Snowflake IDs in the BeforeCreate GORM hook — no UUID dependency. The Snowflake generator is initialized at service startup using the service instance ID.

Request Flow


Configuration

Edit docker/web-api/.web.env before starting the service.

Required variables

VariableRequiredDefaultDescription
SECRET✅ Yesrpd_pksJWT signing secret — must match all services
POSTGRES__HOST✅ YespostgresPostgreSQL host
POSTGRES__DB_NAME✅ Yesweb_dbDatabase name
POSTGRES__AUTH__USER✅ Yesrapida_userDatabase user
POSTGRES__AUTH__PASSWORD✅ YesDatabase password
REDIS__HOST✅ YesredisRedis host
INTEGRATION_HOST✅ Yesintegration-api:9004integration-api gRPC address
ENDPOINT_HOST✅ Yesendpoint-api:9005endpoint-api gRPC address
ASSISTANT_HOST✅ Yesassistant-api:9007assistant-api gRPC address
DOCUMENT_HOST✅ Yeshttp://document-api:9010document-api HTTP address

Tuning variables

VariableDefaultDescription
LOG_LEVELdebugdebug · info · warn · error
ENVdevelopmentdevelopment · staging · production
POSTGRES__MAX_OPEN_CONNECTION10Database connection pool size
POSTGRES__MAX_IDEAL_CONNECTION10Idle connections to keep open
REDIS__MAX_CONNECTION5Redis connection pool size
ASSET_STORE__STORAGE_TYPElocallocal · s3 · azure

Full environment file

# ── Service identity ──────────────────────────────────────────────
SERVICE_NAME=web-api
HOST=0.0.0.0
PORT=9001
LOG_LEVEL=debug
SECRET=rpd_pks
ENV=development

# ── PostgreSQL ────────────────────────────────────────────────────
POSTGRES__HOST=postgres
POSTGRES__PORT=5432
POSTGRES__DB_NAME=web_db
POSTGRES__AUTH__USER=rapida_user
POSTGRES__AUTH__PASSWORD=rapida_db_password
POSTGRES__MAX_OPEN_CONNECTION=10
POSTGRES__MAX_IDEAL_CONNECTION=10
POSTGRES__SSL_MODE=disable

# ── Redis (second-level GORM cache) ───────────────────────────────
POSTGRES__SLC_CACHE__HOST=redis
POSTGRES__SLC_CACHE__PORT=6379
POSTGRES__SLC_CACHE__DB=1
POSTGRES__SLC_CACHE__MAX_CONNECTION=10

# ── Redis ─────────────────────────────────────────────────────────
REDIS__HOST=redis
REDIS__PORT=6379
REDIS__MAX_CONNECTION=5

# ── Asset storage ─────────────────────────────────────────────────
ASSET_STORE__STORAGE_TYPE=local
ASSET_STORE__STORAGE_PATH_PREFIX=/app/rapida-data/assets/web

# ── Internal service addresses ────────────────────────────────────
INTEGRATION_HOST=integration-api:9004
ENDPOINT_HOST=endpoint-api:9005
ASSISTANT_HOST=assistant-api:9007
WEB_HOST=web-api:9001
DOCUMENT_HOST=http://document-api:9010
UI_HOST=https://localhost:3000
SECRET is used for JWT signing and credential vault encryption. All services must share the same value. Changing it in production will invalidate all active sessions and make stored vault credentials unreadable. Rotate carefully.

Running

# Start web-api and its dependencies
make up-web

# Follow logs
make logs-web

# Rebuild after code changes (no cache)
make rebuild-web

# Open a shell in the container
make shell-web

Database Migrations

Migrations run automatically at service startup using golang-migrate. Migration files are in api/web-api/migrations/ and follow sequential naming:
000001_initial_schema.up.sql
000001_initial_schema.down.sql
000002_add_oauth_tokens.up.sql
To run manually during local development:
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

migrate \
  -path api/web-api/migrations \
  -database "postgresql://rapida_user:rapida_db_password@localhost:5432/web_db?sslmode=disable" \
  up

Health & Observability

EndpointPurpose
GET /readiness/Reports whether the service is ready (DB + Redis connected)
GET /healthz/Liveness probe
curl http://localhost:9001/readiness/

Troubleshooting

The most common cause is PostgreSQL not yet healthy. Check make logs-web and confirm the postgres container is Up (healthy).
docker compose ps postgres
make logs-web | head -40
All services must share the same SECRET value. Confirm it is identical in .web.env, .assistant.env, .integration.env, and .endpoint.env.
  • Ensure GOOGLE_CLIENT_ID / GITHUB_CLIENT_ID are set in .web.env.
  • Verify the redirect URI registered with the OAuth provider exactly matches UI_HOST.
  • The target downstream service must be running and healthy.
  • Verify INTEGRATION_HOST, ENDPOINT_HOST, ASSISTANT_HOST point to reachable addresses.
  • Check make status to confirm all containers are Up.

Next Steps