Portagent

Portagent is a multi-user AI Agent gateway powered by AgentX. It provides a web-based interface for interacting with Claude AI agents, with built-in user authentication and session management.
Features
- Multi-User Support: User registration and authentication with JWT tokens
- WebSocket Communication: Real-time bidirectional communication with AI agents
- Invite Code System: Optional invite code for controlled access
- Persistent Storage: SQLite-based storage for users, sessions, and agent data
- Docker Ready: Pre-built Docker images for easy deployment
- Claude Integration: Powered by Anthropic's Claude API via Claude Agent SDK
Quick Start
Using npx (Quick Try)
One-liner to start, requires Node.js 20+:
LLM_PROVIDER_KEY=sk-ant-xxxxx \
LLM_PROVIDER_URL=https://api.anthropic.com \
npx @agentxjs/portagent
Then open http://localhost:5200 in your browser.
Using Docker (Recommended for Production)
docker run -d \
--name portagent \
-p 5200:5200 \
-e LLM_PROVIDER_KEY=sk-ant-xxxxx \
-e LLM_PROVIDER_URL=https://api.anthropic.com \
-v ./data:/home/node/.agentx \
deepracticexs/portagent:latest
Then open http://localhost:5200 in your browser.
Using Docker Compose
Create a .env file:
LLM_PROVIDER_KEY=sk-ant-xxxxx
JWT_SECRET=your-secure-random-secret
Create docker-compose.yml:
services:
portagent:
image: deepracticexs/portagent:latest
container_name: portagent
restart: unless-stopped
ports:
- "5200:5200"
environment:
- LLM_PROVIDER_KEY=${LLM_PROVIDER_KEY}
- LLM_PROVIDER_URL=${LLM_PROVIDER_URL:-https://api.anthropic.com}
- LLM_PROVIDER_MODEL=${LLM_PROVIDER_MODEL:-claude-sonnet-4-20250514}
- JWT_SECRET=${JWT_SECRET}
- INVITE_CODE_REQUIRED=${INVITE_CODE_REQUIRED:-false}
- LOG_LEVEL=${LOG_LEVEL:-info}
volumes:
- ./data:/home/node/.agentx
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5200/health"]
interval: 30s
timeout: 10s
start_period: 5s
retries: 3
Run:
docker compose up -d
Using npm
npm install -g @agentxjs/portagent
export LLM_PROVIDER_KEY=sk-ant-xxxxx
export LLM_PROVIDER_URL=https://api.anthropic.com
portagent
Configuration
Environment Variables
LLM_PROVIDER_KEY | Yes | - | Anthropic API key (starts with sk-ant-) |
LLM_PROVIDER_URL | No | https://api.anthropic.com | API base URL |
LLM_PROVIDER_MODEL | No | claude-sonnet-4-20250514 | Claude model to use |
PORT | No | 5200 | Server port |
AGENTX_DIR | No | ~/.agentx | AgentX data directory path |
JWT_SECRET | No | Auto-generated | Secret for JWT token signing |
INVITE_CODE_REQUIRED | No | false | Require invite code for registration |
LOG_LEVEL | No | info | Log level: debug, info, warn, error |
NODE_ENV | No | production | Environment mode |
CLI Options
portagent [options]
Options:
-p, --port <port> Port to listen on (default: 5200)
-d, --data-dir <path> Data directory (default: ~/.agentx)
-e, --env-file <path> Path to environment file
--jwt-secret <secret> JWT secret for token signing
--api-key <key> LLM provider API key
--api-url <url> LLM provider base URL
--model <model> LLM model name
-h, --help Display help
-V, --version Display version
Data Directory Structure
~/.agentx/ # Default data directory (configurable via AGENTX_DIR)
├── data/ # Database files
│ ├── agentx.db # AgentX data (containers, images, sessions)
│ └── portagent.db # User authentication data
└── logs/ # Log files
└── portagent.log
Invite Code System
Portagent uses a daily rotating invite code for registration security. The invite code is the Unix timestamp (in seconds) of today's 00:00:01 in the server's timezone.
Calculating the Invite Code
Linux/macOS:
date -d "today 00:00:01" +%s
TZ=UTC date -d "today 00:00:01" +%s
date -j -f "%Y-%m-%d %H:%M:%S" "$(date +%Y-%m-%d) 00:00:01" "+%s"
JavaScript:
const now = new Date();
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 1);
const inviteCode = Math.floor(todayStart.getTime() / 1000);
console.log(inviteCode);
Note: Docker containers typically run in UTC timezone. Make sure to calculate the invite code for the correct timezone.
Enabling Invite Codes
Set INVITE_CODE_REQUIRED=true to require invite codes for registration:
docker run -e INVITE_CODE_REQUIRED=true ...
API Endpoints
| GET | /health | No | Health check |
| GET | /api/auth/config | No | Get auth configuration |
| POST | /api/auth/register | No | Register new user |
| POST | /api/auth/login | No | Login |
| GET | /api/auth/verify | Yes | Verify token |
| POST | /api/auth/logout | No | Logout (client-side) |
| GET | /agentx/info | Yes | Get platform info |
| WS | /ws | Yes | WebSocket connection |
Authentication
All protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <token>
For WebSocket connections (which don't support headers), pass the token as a query parameter:
ws://localhost:5200/ws?token=<token>
Register
curl -X POST http://localhost:5200/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "john",
"password": "secret123",
"inviteCode": "1765152001",
"email": "john@example.com",
"displayName": "John Doe"
}'
Login
curl -X POST http://localhost:5200/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"usernameOrEmail": "john",
"password": "secret123"
}'
Docker Images
Pre-built images are available on Docker Hub:
docker pull deepracticexs/portagent:latest
docker pull deepracticexs/portagent:0.1.9
Building Locally
docker build -t portagent:local -f apps/portagent/Dockerfile .
docker run -d \
--name portagent \
-p 5200:5200 \
-e LLM_PROVIDER_KEY=sk-ant-xxxxx \
portagent:local
Production Deployment
Recommended Settings
services:
portagent:
image: deepracticexs/portagent:0.1.9
restart: unless-stopped
environment:
- LLM_PROVIDER_KEY=${LLM_PROVIDER_KEY}
- JWT_SECRET=${JWT_SECRET}
- INVITE_CODE_REQUIRED=true
- LOG_LEVEL=info
volumes:
- ./data:/home/node/.agentx
ports:
- "5200:5200"
Security Considerations
- API Key: Never expose
LLM_PROVIDER_KEY in client-side code or logs
- JWT Secret: Use a strong, random secret and keep it consistent across restarts
- Invite Codes: Enable invite codes in production to control access
- HTTPS: Use a reverse proxy (nginx, Caddy) with TLS in production
- Volume Permissions: The container runs as non-root user
node
Reverse Proxy (nginx example)
server {
listen 443 ssl;
server_name portagent.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:5200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Troubleshooting
"LLM_PROVIDER_KEY is required"
- Ensure
LLM_PROVIDER_KEY environment variable is set
- For Docker, use
-e LLM_PROVIDER_KEY=xxx or environment: in compose
"Invalid invite code"
- Invite code changes daily at midnight (server timezone)
- Docker uses UTC by default
- Calculate code for correct timezone (see Invite Code section)
- Set
INVITE_CODE_REQUIRED=false to disable
Permission denied errors
- Docker container runs as user
node
- Ensure mounted volumes have correct permissions:
sudo chown -R $(id -u):$(id -g) ./data
WebSocket connection fails
- Ensure token is passed as query parameter for WS connections
- Check reverse proxy WebSocket configuration
Viewing Logs
docker logs portagent
docker logs -f portagent
docker exec portagent cat /home/node/.agentx/logs/portagent.log
Development
git clone https://github.com/Deepractice/AgentX.git
cd AgentX
pnpm install
pnpm build
cd apps/portagent
pnpm dev
License
MIT License - see LICENSE for details.