A self-hosted APK build service for generating signed Android APKs from web applications.
This service provides a remote build server that can:
- Build Android APKs from WebView-wrapped web applications
- Sign APKs with provided keystores
- Authenticate users via Nostr NIP-98
# Clone the repository
git clone https://github.com/user/android-build-server.git
cd android-build-server
# Run setup (generates API key, builds Docker image, starts service)
./setup.sh
# Check health
curl http://localhost:3000/healthThe setup script creates a .env file with these settings:
PORT- Service port (default: 3000)API_KEY- Admin authentication keyMAX_CONCURRENT_BUILDS- Parallel builds (default: 2)BUILD_TIMEOUT_MS- Build timeout (default: 600000)CORS_ORIGINS- Allowed origins (comma-separated, or*for all)
The server supports two authentication methods:
- Admin API Key - Set in
.env, passed viaX-API-Keyheader - NIP-98 Nostr Auth - Users can request personal API keys using their Nostr identity
Users can authenticate with their Nostr identity to get a personal API key:
# POST /api/auth with NIP-98 Authorization header
curl -X POST http://localhost:3000/api/auth \
-H "Authorization: Nostr <base64-encoded-kind-27235-event>"curl http://localhost:3000/health
# Returns: { "status": "ok" }curl http://localhost:3000/api/stats \
-H "X-API-Key: YOUR_ADMIN_API_KEY"Returns build statistics including:
{
"status": "ok",
"version": "1.1.0",
"builds": {
"total": 42,
"successful": 38,
"failed": 3,
"cancelled": 1,
"active": 2,
"lastBuildAt": "2025-01-04T12:34:56.789Z"
},
"queue": { "queued": 1, "processing": 1 },
"uptime": 3600,
"startedAt": "2025-01-01T00:00:00.000Z"
}curl -X POST http://localhost:3000/api/build \
-H "X-API-Key: YOUR_API_KEY" \
-F "project=@dist.zip" \
-F 'config={"appName":"My App","packageId":"com.example.app"}'curl http://localhost:3000/api/build/{buildId}/status \
-H "X-API-Key: YOUR_API_KEY"curl -o app.apk http://localhost:3000/api/build/{buildId}/download \
-H "X-API-Key: YOUR_API_KEY"curl http://localhost:3000/api/builds \
-H "X-API-Key: YOUR_API_KEY"curl -X DELETE http://localhost:3000/api/build/{buildId} \
-H "X-API-Key: YOUR_API_KEY"# Start the service
docker compose up -d
# Stop the service
docker compose down
# View logs
docker compose logs -f
# Rebuild after updates
docker compose up -d --build
# Run test build
./test.shThe server applies rate limiting to prevent abuse:
| Endpoint | Limit | Window |
|---|---|---|
General API (/api/*) |
120 requests | 1 minute |
Authentication (/api/auth) |
10 requests | 1 minute |
Build submission (POST /api/build) |
20 requests | 1 hour |
Additionally, each user is limited to 3 concurrent active builds.
- Users can only access their own builds (status, download, cancel)
- Admin API key can access all builds
- Build records are automatically cleaned up after 1 hour
- ZIP files are validated for magic bytes before processing
- Path traversal (ZIP Slip) attacks are blocked
- App names and package IDs are sanitized
- File size limits enforced (100MB upload, 50MB per file in ZIP)
- Builds run in Docker containers
- Non-root user execution (
apkbuild) - Environment variables filtered (secrets not passed to Gradle)
- npm lifecycle scripts disabled (
--ignore-scripts)
- Build directories cleaned immediately after completion
- APK files automatically deleted after 1 hour
- Orphaned APKs cleaned on server startup
android-build-server/
├── Dockerfile # Docker image with Android SDK
├── docker-compose.yml # Container configuration
├── package.json # Node.js dependencies
├── server.js # Express API server
├── lib/
│ ├── auth.js # NIP-98 authentication
│ ├── builder.js # APK build logic
│ ├── queue.js # Build queue management
│ └── stats.js # Persistent build statistics
├── setup.sh # Setup script
├── test.sh # Test script
└── .env # Configuration (generated)
See PLAN.md for the full architecture and implementation plan.
MIT