diff --git a/blueprints/orbit/docker-compose.yml b/blueprints/orbit/docker-compose.yml new file mode 100644 index 00000000..200d0db1 --- /dev/null +++ b/blueprints/orbit/docker-compose.yml @@ -0,0 +1,215 @@ +services: + postgres: + image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 + environment: + POSTGRES_USER: orbit + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required} + POSTGRES_DB: orbit + volumes: + - postgres:/var/lib/postgresql + healthcheck: + test: + - CMD-SHELL + - pg_isready -U orbit -d orbit + interval: 5s + timeout: 3s + retries: 20 + restart: unless-stopped + redis: + image: redis:8-alpine@sha256:978f0e01593e65eed801f2402944efcd936d43b5027e4908a7897baf88ed6241 + command: + - redis-server + - --appendonly + - 'yes' + volumes: + - redis:/data + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 3s + retries: 20 + restart: unless-stopped + storage: + image: quay.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e + command: + - server + - /data + environment: + MINIO_ROOT_USER: orbit + MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD:?required} + MINIO_API_CORS_ALLOW_ORIGIN: ${ORBIT_APP_URL:?required} + volumes: + - storage:/data + healthcheck: + test: + - CMD + - mc + - ready + - local + interval: 5s + timeout: 3s + retries: 20 + restart: unless-stopped + expose: + - '9000' + bucket: + image: ghcr.io/noveum/orbit-bucket:sha-7da6cdb83550c4e597800b2788c4ce534d1af669 + environment: + MINIO_PASSWORD: ${MINIO_PASSWORD:?required} + ORBIT_APP_URL: ${ORBIT_APP_URL:?required} + depends_on: + storage: + condition: service_healthy + restart: unless-stopped + healthcheck: + test: + - CMD-SHELL + - test -f /tmp/initialized + interval: 5s + timeout: 3s + retries: 30 + web: + image: ghcr.io/noveum/orbit-runtime:sha-7da6cdb83550c4e597800b2788c4ce534d1af669 + environment: + DATABASE_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit + DIRECT_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit + DATABASE_PREPARED_STATEMENTS: 'true' + REDIS_URL: redis://redis:6379 + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?required} + BETTER_AUTH_URL: ${ORBIT_APP_URL:?required} + NEXT_PUBLIC_APP_URL: ${ORBIT_APP_URL:?required} + ORBIT_PASSWORD_AUTH: ${ORBIT_PASSWORD_AUTH:-true} + ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-} + RESEND_API_KEY: ${RESEND_API_KEY:-} + EMAIL_FROM: ${EMAIL_FROM:-Orbit } + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} + GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-} + GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-} + GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-} + GITHUB_APP_ID: ${GITHUB_APP_ID:-} + GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-} + GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-} + GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-} + GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-} + SLACK_ENABLED: ${SLACK_ENABLED:-false} + SLACK_CLIENT_ID: ${SLACK_CLIENT_ID:-} + SLACK_CLIENT_SECRET: ${SLACK_CLIENT_SECRET:-} + SLACK_SIGNING_SECRET: ${SLACK_SIGNING_SECRET:-} + SLACK_APP_ID: ${SLACK_APP_ID:-} + CRON_SECRET: ${CRON_SECRET:-} + ORBIT_SELF_HOSTED: 'true' + S3_ENDPOINT: ${ORBIT_STORAGE_URL:?required} + S3_REGION: us-east-1 + S3_BUCKET: orbit-uploads + S3_ACCESS_KEY_ID: orbit + S3_SECRET_ACCESS_KEY: ${MINIO_PASSWORD:?required} + NEXT_TELEMETRY_DISABLED: '1' + healthcheck: + test: + - CMD + - node + - -e + - fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1)) + interval: 10s + timeout: 5s + start_period: 30s + retries: 5 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + bucket: + condition: service_healthy + restart: unless-stopped + realtime: + image: ghcr.io/noveum/orbit-runtime:sha-7da6cdb83550c4e597800b2788c4ce534d1af669 + command: + - node + - apps/web/realtime.mjs + environment: + DATABASE_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit + DIRECT_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit + DATABASE_PREPARED_STATEMENTS: 'true' + REDIS_URL: redis://redis:6379 + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?required} + BETTER_AUTH_URL: ${ORBIT_APP_URL:?required} + NEXT_PUBLIC_APP_URL: ${ORBIT_APP_URL:?required} + ORBIT_PASSWORD_AUTH: ${ORBIT_PASSWORD_AUTH:-true} + ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-} + RESEND_API_KEY: ${RESEND_API_KEY:-} + EMAIL_FROM: ${EMAIL_FROM:-Orbit } + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} + GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-} + GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-} + GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-} + GITHUB_APP_ID: ${GITHUB_APP_ID:-} + GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-} + GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-} + GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-} + GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-} + SLACK_ENABLED: ${SLACK_ENABLED:-false} + SLACK_CLIENT_ID: ${SLACK_CLIENT_ID:-} + SLACK_CLIENT_SECRET: ${SLACK_CLIENT_SECRET:-} + SLACK_SIGNING_SECRET: ${SLACK_SIGNING_SECRET:-} + SLACK_APP_ID: ${SLACK_APP_ID:-} + CRON_SECRET: ${CRON_SECRET:-} + ORBIT_SELF_HOSTED: 'true' + S3_ENDPOINT: ${ORBIT_STORAGE_URL:?required} + S3_REGION: us-east-1 + S3_BUCKET: orbit-uploads + S3_ACCESS_KEY_ID: orbit + S3_SECRET_ACCESS_KEY: ${MINIO_PASSWORD:?required} + NEXT_TELEMETRY_DISABLED: '1' + healthcheck: + test: + - CMD + - node + - -e + - fetch('http://127.0.0.1:3100/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1)) + interval: 10s + timeout: 5s + retries: 5 + depends_on: + web: + condition: service_healthy + bucket: + condition: service_healthy + restart: unless-stopped + scheduler: + image: ghcr.io/noveum/orbit-runtime:sha-7da6cdb83550c4e597800b2788c4ce534d1af669 + command: + - node + - apps/web/scheduler.mjs + environment: + CRON_SECRET: ${CRON_SECRET:?required} + ORBIT_INTERNAL_URL: http://web:3000 + stop_grace_period: 310s + depends_on: + web: + condition: service_healthy + restart: unless-stopped + orbit: + image: ghcr.io/noveum/orbit-gateway:sha-7da6cdb83550c4e597800b2788c4ce534d1af669 + environment: + ORBIT_TRUSTED_PROXIES: ${ORBIT_TRUSTED_PROXIES:-private_ranges} + depends_on: + web: + condition: service_healthy + realtime: + condition: service_healthy + scheduler: + condition: service_started + restart: unless-stopped + expose: + - '3000' +volumes: + postgres: null + redis: null + storage: null +version: '3.8' diff --git a/blueprints/orbit/instructions.md b/blueprints/orbit/instructions.md new file mode 100644 index 00000000..50a57058 --- /dev/null +++ b/blueprints/orbit/instructions.md @@ -0,0 +1,13 @@ +# Orbit by Noveum + +Deploy, then enable HTTPS with a valid certificate for both generated domains +in Dokploy's Domains tab. Open the Orbit HTTPS domain and create your account +and workspace. Both the app and storage domains require working HTTPS. Database migrations, +bucket creation and the storage CORS allowlist are configured automatically. +PostgreSQL, Redis and MinIO use persistent volumes. Preserve generated secrets +and back up PostgreSQL and MinIO before upgrades. Run one scheduler instance. +Configure Resend and EMAIL_FROM on web to send invitations and password recovery. +Configure ALLOWED_EMAIL_DOMAINS to restrict signup to your team. + +Hosted MCP: https://orbit.noveum.ai/mcp +Self-hosted MCP: your Orbit HTTPS domain followed by /mcp. diff --git a/blueprints/orbit/meta.json b/blueprints/orbit/meta.json new file mode 100644 index 00000000..78153d03 --- /dev/null +++ b/blueprints/orbit/meta.json @@ -0,0 +1,18 @@ +{ + "id": "orbit", + "name": "Orbit by Noveum", + "version": "sha-7da6cdb83550c4e597800b2788c4ce534d1af669", + "description": "Open-source task management with issues, projects, sprints, docs, files, realtime collaboration and an OAuth MCP endpoint.", + "logo": "orbit.png", + "links": { + "github": "https://github.com/Noveum/orbit", + "website": "https://orbit.noveum.ai", + "docs": "https://github.com/Noveum/orbit/blob/7da6cdb83550c4e597800b2788c4ce534d1af669/docs/container-catalogs.md" + }, + "tags": [ + "productivity", + "project-management", + "collaboration", + "mcp" + ] +} diff --git a/blueprints/orbit/orbit.png b/blueprints/orbit/orbit.png new file mode 100644 index 00000000..c7adba31 Binary files /dev/null and b/blueprints/orbit/orbit.png differ diff --git a/blueprints/orbit/template.toml b/blueprints/orbit/template.toml new file mode 100644 index 00000000..74bdbc88 --- /dev/null +++ b/blueprints/orbit/template.toml @@ -0,0 +1,27 @@ +[variables] +main_domain = "${domain}" +storage_domain = "storage-${domain}" +postgres_password = "${password:48}" +minio_password = "${password:48}" +auth_secret = "${password:64}" +cron_secret = "${password:64}" + +[config] +env = [ + "ORBIT_APP_URL=https://${main_domain}", + "ORBIT_STORAGE_URL=https://${storage_domain}", + "POSTGRES_PASSWORD=${postgres_password}", + "MINIO_PASSWORD=${minio_password}", + "BETTER_AUTH_SECRET=${auth_secret}", + "CRON_SECRET=${cron_secret}", +] + +[[config.domains]] +serviceName = "orbit" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "storage" +port = 9000 +host = "${storage_domain}"