Base66 is a Spring Boot application with a built-in web UI for authenticated chat via OpenCode. It includes JWT login, per-user workspace provisioning, chat sessions, permission handling, and skill creation from free text.
- JWT-based authentication
- Chat endpoint backed by OpenCode
- Session APIs: list, create, select, rename, delete
- Pending permission polling and reply flow
- Skill creation endpoint that writes SKILL.md files in user workspace
- Optional OpenCode auto-start on application startup
- Static frontend served from
/
- Java 21
- Spring Boot 4
- Spring Security (JWT)
- Spring Web + WebFlux
- Spring AI abstractions
- Maven Wrapper
- Java 21 installed
- OpenCode available locally or reachable via URL
Application properties load .env through:
spring.config.import=optional:file:.env[.properties]
Required environment variables:
OPENCODE_BASE_URLAPP_AUTH_JWT_SECRETAPP_AUTH_JWT_EXPIRATION_SECONDS
Optional OpenCode process variables:
OPENCODE_AUTOSTART(default:true)OPENCODE_COMMAND(default:opencode)OPENCODE_ARGS(default:serve --hostname 127.0.0.1 --port 4096)OPENCODE_WORKING_DIRECTORY(default: empty)OPENCODE_STARTUP_TIMEOUT_SECONDS(default:30)OPENCODE_STOP_ON_SHUTDOWN(default:true)
OPENCODE_BASE_URL=http://localhost:4096
APP_AUTH_JWT_SECRET=replace-with-a-strong-secret
APP_AUTH_JWT_EXPIRATION_SECONDS=3600From the project root:
.\mvnw.cmd spring-boot:runThen open:
Configured in application.properties:
user1 / pass1user2 / pass2
POST /api/auth/loginis public.- All
/api/base66/**endpoints require:Authorization: Bearer <token>
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user1","password":"pass1"}'POST /api/base66/chat- Request:
{ "message": "..." } - Response:
{ "response": "..." }
- Request:
GET /api/base66/sessions?limit=20POST /api/base66/sessionswith optional{ "title": "..." }POST /api/base66/sessions/{sessionId}/selectPATCH /api/base66/sessions/{sessionId}with{ "title": "..." }DELETE /api/base66/sessions/{sessionId}
GET /api/base66/permissions/pendingPOST /api/base66/permissions/{requestId}/reply- Request:
{ "reply": "once" | "always" | "reject" }
- Request:
-
GET /api/base66/skills- Response:
[{ "name": "...", "description": "..." }, ...]
- Response:
-
GET /api/base66/skills/{skillName}- Response:
{ "name": "...", "description": "...", "content": "..." }
- Response:
-
POST /api/base66/skills- Request:
{ "skillName": "...", "description": "...", "content": "..." } - Response:
{ "name": "...", "description": "...", "content": "..." }
- Request:
-
PUT /api/base66/skills/{skillName}- Request:
{ "description": "...", "content": "..." } - Response:
{ "name": "...", "description": "...", "content": "..." }
- Request:
-
DELETE /api/base66/skills/{skillName}- Response:
{ "success": true|false }
- Response:
401 Unauthorized: missing/invalid/expired token400 Bad Request: validation or malformed request502 Bad Gateway: OpenCode returned an upstream error503 Service Unavailable: OpenCode endpoint not reachable
- User workspaces are provisioned under
app.user-workspaces-root. - On startup, Base66 can:
- Ensure OpenCode is running (when autostart is enabled)
- Prepare configured user workspaces
- Validate required per-user OpenCode agent setup
src/main/java/com/akatsuki/base66/controller- REST controllerssrc/main/java/com/akatsuki/base66/service- business logic and integrationssrc/main/java/com/akatsuki/base66/security- JWT security componentssrc/main/resources/application.properties- app configsrc/main/resources/static/index.html- frontend UI