1
0
Fork
You've already forked unimo-gateway
0
⚠️ WIP / NOT READY FOR USE!
  • TypeScript 91.5%
  • HTML 5.5%
  • Makefile 1.7%
  • Shell 1.2%
  • Dockerfile 0.1%
2026年06月24日 09:53:46 +02:00
.forgejo/workflows feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
deploy add caddy config 2026年06月01日 21:31:55 +02:00
docker fix docker image port 2026年06月01日 15:18:36 +02:00
docs migration to new s3 key format, support upload chunks and lot of changes around in vault mgmt and tasker, etc 2026年05月11日 20:57:42 +02:00
entry S3 orchestration + redis caching 2026年04月20日 12:06:07 +02:00
image ci: fix docker image for local dev 2026年05月05日 13:43:03 +02:00
scripts feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
src increase req limits defaults 2026年06月24日 09:19:45 +02:00
tests/e2e add invites support 2026年05月21日 13:03:07 +02:00
tools feat: rewrite sharding support, add lattice sdk, ci fixes, etc 2026年05月07日 17:16:06 +02:00
ui get presigned urls + demo page 2026年05月19日 12:29:12 +02:00
.dockerignore ci: fix docker image for local dev 2026年05月05日 13:43:03 +02:00
.env.example increase req limits defaults 2026年06月24日 09:19:45 +02:00
.gitignore feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
bun.lock update bun lock 2026年06月24日 09:53:46 +02:00
cosign.pub feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
LICENSE update bun lock and license 2026年06月11日 07:32:01 +02:00
Makefile feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
package.json bump version, update modules 2026年06月24日 09:36:31 +02:00
README.md minor fixies in readme and env example 2026年06月01日 14:36:26 +02:00
TODO-TDX-ATTESTATION.md feat: draft TDX support with make 2026年05月04日 16:30:09 +02:00
tsconfig.json typecheck no unused vars 2026年05月20日 09:47:25 +02:00
vitest.config.ts feat: update storage, tests, acount mgmt, etc 2026年05月04日 09:09:30 +02:00

unimo-gateway

Privacy-preserving gateway for search, LLM proxy, user management, and encrypted file sync. Currently implements search routing through Brave, Serper, and Google Suggest without exposing user identity. Semantic caching via embeddings + LSH. Domain-prefixed WebSocket protocol. Runs on Bun + Valkey (Docker). Optional SGX attestation.

Setup

git clone git@codeberg.org:thinking_tools/secure.gateway.unimo.git && cd secure.gateway.unimo
cp .env.example .env

Edit .env:

BRAVE_API_KEYS=key1 key2 # space-separated, round-robin rotation
SERPER_API_KEYS=key1 key2
REDIS_URL=redis://token@host:port # optional, for non-Docker setups
VALKEY_PASSWORD= # optional, Docker Valkey auth
PORT=3000

Get API keys: Brave Search | Serper

Run

Local (Bun)

bun install
bun run dev

REDIS_URL is optional — the app falls back to no caching without it.

Docker

See Docker section below.

Usage

Protected routes use PQ-issued session tokens: register an account, then POST /api/auth/login to mint a token, sent as Authorization: Bearer <token> with x-vault-id and x-member-id headers (the WebSocket upgrade accepts ?token=&vid=&mid= query params instead, since browsers can't set headers on WebSocket). GET / is public; admin routes require GATEWAY_ADMIN_TOKEN.

WebSocket — /api/search/ws

Connect and interact in real-time:

const ws = new WebSocket('ws://localhost:3000/api/search/ws?token=your-secret');
ws.onopen = () => {
 // Get suggestions as you type
 ws.send(JSON.stringify({ type: 'search:typing', id: '1', query: 'hello w' }));
 // Full search
 ws.send(JSON.stringify({ type: 'search:query', id: '2', query: 'hello world' }));
};
ws.onmessage = ({ data }) => {
 const msg = JSON.parse(data);
 // msg.type: 'ready' | 'search:suggestions' | 'search:results' | 'error'
};

Messages

Direction Type Payload
client → hello { type, id? }
client → search:typing { type, id, query }
client → search:query { type, id, query, params? }
← server ready { type, version, commit, buildTime }
← server search:suggestions { type, id, suggestions: string[] }
← server search:results { type, id, data: UnifiedSearchResponse, cached }
← server error { type, id, error: string }

REST

# Health check
curl http://localhost:3000/
# SGX attestation (when running in enclave)
curl http://localhost:3000/api/attestation/quote?token=your-secret
curl http://localhost:3000/api/attestation/mrenclave?token=your-secret

Docker

Both setups use Valkey as the Redis-compatible cache layer with AOF persistence enabled (appendfsync everysec), so data survives container restarts. REDIS_URL is auto-configured by compose — no need to set it in .env. Set VALKEY_PASSWORD in .env to enable Valkey authentication.

Docker files live in docker/ — all commands use --project-directory . so relative paths resolve from the repo root.

Dev

docker compose --project-directory . -f docker/compose.yaml -f docker/compose.override.yaml up

Auto-loads dev overrides — source-mounted with hot reload, low resource caps (768MB / 2 CPU per service). Installs deps and generates a fallback version.ts inside the container automatically. No npm install needed on the host.

Valkey is exposed on localhost:6379 for local debugging.

Production

# Build (pass current commit hash)
docker compose --project-directory . -f docker/compose.yaml -f docker/compose.prod.yaml build \
 --build-arg GIT_COMMIT=$(git rev-parse --short HEAD)
# Run
docker compose --project-directory . -f docker/compose.yaml -f docker/compose.prod.yaml up -d
# Logs
docker compose --project-directory . -f docker/compose.yaml -f docker/compose.prod.yaml logs -f
# Stop
docker compose --project-directory . -f docker/compose.yaml -f docker/compose.prod.yaml down

Builds a multi-stage image (production deps only, non-root user). Valkey runs with 28GB maxmemory, AOF persistence (everysec), 65k file descriptors, and allkeys-lru eviction. Gateway has HTTP healthchecks on / and JSON log rotation.

Test

bun test

Architecture

Client ──WebSocket──▶ Hono App ──▶ MessageRouter ──▶ Domain Handlers
 ├── system: hello/ready
 └── search: SearchEngine
 ├── Google Suggest (free, fast)
 ├── Serper (backfill + preload)
 ├── Brave Search (full results)
 └── Semantic Cache (Redis + LSH embeddings)
  • Suggest flow (search:typing): exact cache → Google Suggest → background Serper preload
  • Search flow (search:query): semantic cache lookup → parallel Brave search + suggestions → background cache write
  • Embeddings: embeddinggemma-300m via HuggingFace ONNX
  • Caching: 256-dim truncated embeddings, 12-table LSH, cosine similarity ≥ 0.82, 3-day TTL