TO TRY OUT:
Links:
To populate DB: https://unbounds-production.up.railway.app/docs
To interact with frontend(mock commands): https://unbounds.vercel.app/
To view video: https://drive.google.com/drive/folders/1tANyrn7s4aP5AGnUUB5Kt0PfiXVkb-gG?usp=sharing
Sample Keys:
Admin: db5a787ee0d5c15df365423c21aa65e0
Senior: 120cb523ce6086b8d7c3feb8cbcf08b7
Junior: 87c1f9547bb3da8f6a5849528e08ffb2
A complete Command Gateway system: FastAPI backend, approval worker, React frontend.
Features: API-key auth, credits, rule engine (regex), approvals, voting thresholds, escalation, time-based rules, email notifications, analytics.
See /backend, /worker, /frontend folders for service implementations.
cd backend
pip install -r requirements.txt
export DATABASE_URL=./db.sqlite # or $env:DATABASE_URL="./db.sqlite" on Windows PowerShell
uvicorn main:app --reload --port 10000On startup, the backend prints the default admin API key to logs. Copy it.
cd worker
pip install -r requirements.txt
export DATABASE_URL=../backend/db.sqlite
python worker.pycd frontend
npm install
export VITE_API_URL=http://localhost:10000 # or $env:VITE_API_URL="..." on Windows
npm run devThen open http://localhost:5173 and paste the admin API key.
- FastAPI service with SQLModel ORM + SQLite database
- Models: User (roles: admin/member/approver), Rule (regex-based), Command, Approval, ApprovalVote, EventLog
- Endpoints:
POST /users— admin creates usersGET /rules,POST /rules— list/create approval rulesPOST /commands— submit a command (triggers rule matching, approval flow, or auto-accept/reject)GET /commands— view command historyPOST /approvals/{id}/vote— approver votes to approve/reject pending command
- Authentication: x-api-key header
- Flow:
- User submits command → backend matches against rules by regex priority
- Rule determines action: AUTO_ACCEPT, AUTO_REJECT, or REQUIRE_APPROVAL
- If REQUIRE_APPROVAL: create Approval record with threshold (default 2 votes)
- Approvers vote; once threshold met, command executes and deducts credits
- All actions logged to EventLog for audit
- Background job that runs every 60s
- Checks pending approvals: escalates expired ones, auto-rejects very old ones (60+ min)
- Two modes:
- Shared DB mode (Render): reads SQLite directly from persistent disk
- API mode (Railway): calls backend endpoints via HTTP (default)
- React + Vite minimal SPA
- Pages:
LoginKey.jsx— enter API keyDashboard.jsx— submit commands, view historyAdminRules.jsx— (stub) create/list rulesApprovals.jsx— (stub) vote on pending approvals
- API wrapper:
api.jsexports apiClient function that injects x-api-key header
- RENDER_DEPLOYMENT.md — Recommended for simplicity (SQLite, free tier available)
- RAILWAY_DEPLOYMENT.md — Recommended for scalability (PostgreSQL, auto-scaling)
-
Backend (choice of Render or Railway)
- Render: Web Service with persistent disk at
/data(SQLite) - Railway: Service with PostgreSQL plugin (auto-provisioned DATABASE_URL)
- Render: Web Service with persistent disk at
-
Worker (choice of Render or Railway)
- Render: Background Job or Web Service (same disk as backend, DB mode)
- Railway: Service with API mode enabled (calls backend endpoints)
-
Frontend (Vercel)
- Import GitHub repo → set root to
frontend/ - Set
VITE_API_URLenv to your backend URL
- Import GitHub repo → set root to
-
SendGrid (email notifications)
- Create account → generate API key → set
SENDGRID_API_KEYenv
- Create account → generate API key → set
Render (SQLite + shared disk):
DATABASE_URL=/data/db.sqlite
SENDGRID_API_KEY=sg-...
Railway (PostgreSQL + API-mode worker):
Backend:
DATABASE_URL=postgresql://... # Auto-provisioned by Railway Postgres
SENDGRID_API_KEY=sg-...
Worker:
WORKER_MODE=api
BACKEND_URL=https://your-backend.railway.app
WORKER_API_KEY=<admin-key-from-backend-logs>
SENDGRID_API_KEY=sg-...
Frontend (both platforms):
VITE_API_URL=https://your-backend-url
- Backend starts → prints admin API key to logs
- Copy key → login to frontend with it
- Create users:
POST /userswith admin key (creates new member/approver) - Create rules:
POST /ruleswith regex patterns (e.g.,rm -rf /→ AUTO_REJECT)
- User logs in with their API key
- Submit command (e.g.,
ls -la) - Backend matches rules:
- If rule matches and action is AUTO_ACCEPT → command executed, credits deducted, done
- If rule matches and action is AUTO_REJECT → command rejected
- If no rule or action is REQUIRE_APPROVAL → create Approval record, send Telegram to admins
- Approver logs in with approver key
- Sees pending approvals → votes APPROVE (if threshold met, command executes)
- Worker checks every 60s
- If approval expires (10 min default) → marked escalated, Telegram sent
- If very old (60+ min) → auto-rejected
# In separate terminals:
# Terminal 1: Backend
cd backend && uvicorn main:app --reload --port 10000
# Terminal 2: Worker
cd worker && python worker.py
# Terminal 3: Frontend
cd frontend && npm run devThen:
- Open http://localhost:5173
- Paste admin key from backend logs
- Create a member user via API (or use admin)
- Submit
ls -la→ should auto-accept if no rules - Submit
rm -rf /→ should reject if rule exists - Submit
unknown-command→ should create approval request
command-gateway/
├── backend/
│ ├── main.py # FastAPI app, endpoints
│ ├── models.py # SQLModel definitions
│ ├── crud.py # CRUD functions
│ ├── db.py # SQLModel engine + session
│ ├── schemas.py # Pydantic request/response schemas
│ ├── notifications.py # SendGrid + Telegram helpers
│ ├── requirements.txt
│ ├── Dockerfile
│ └── render.yaml
├── worker/
│ ├── worker.py # Background approval scheduler
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── src/
│ │ ├── main.jsx # React entry point
│ │ ├── App.jsx # Root component (auth + routing)
│ │ ├── api.js # Axios client wrapper
│ │ ├── pages/
│ │ │ ├── Dashboard.jsx
│ │ │ ├── AdminRules.jsx
│ │ │ └── Approvals.jsx
│ │ └── components/
│ │ ├── LoginKey.jsx
│ │ └── CommandForm.jsx
│ ├── package.json
│ ├── vite.config.js
│ └── README_FRONTEND.md
└── README.md
- All services communicate over HTTP (backend API). No shared message queue or Kafka.
- Worker uses shared SQLite DB (works on Render with persistent disk). For Railway (no shared disk), modify worker to call backend API endpoints instead.
- Credits are user balance; deducted on command execution. No credit refunds on rejection.
- EventLog captures all significant events for audit/analytics.
- Email notifications via SendGrid are best-effort (no retry on failure; logged to stderr).
- Expand frontend with admin and approver full pages
- Add Docker Compose for local dev
- Add unit tests and Cypress E2E tests
- Migrate to Postgres for production
- Add rate-limiting and RBAC enforcement