Swadhin Biswas

[email protected]
2026-08-02 16:00:46 +0600 committed NaN years ago
Co-auth
commit Co-auth
336 files changed
+33834 -9238
Showing 75 changed files+5196-3318
This diff is too large and has been truncated. Displaying first 75 files.
.astro/settings.json.env.example.github/workflows/ai-review.yml.github/workflows/ci.yml.github/workflows/docker-publish.yml.gitignore.mimocode/.cron-lock.mimocode/plans/1784309305424-sunny-pixel.mdAGENTS.mdDEPLOYMENT.mdDOCKERHUB.mdDockerfileDockerfile.workerPRODUCTION_READINESS.mdREADME.mdVercel Deploy.mdastro.config.mjsbun.lockcli/src/commands/stack/index.tsdeploy/prometheus.ymldoc/docs-parity-baseline.jsondocker-compose.nas.ymldocker-compose.production.ymldocker-compose.ymldocker-entrypoint.shdocs-site/README.mddocs-site/astro.config.mjsdocs-site/src/content/docs/FREE-DEPLOYMENT.mddocs-site/src/content/docs/RENDER-DEPLOYMENT.mddocs-site/src/content/docs/administration/air-gapped.mddocs-site/src/content/docs/administration/backup-restore.mddocs-site/src/content/docs/administration/configuration.mddocs-site/src/content/docs/administration/deploy-nas.mddocs-site/src/content/docs/administration/deployment.mddocs-site/src/content/docs/administration/expose-nas.mddocs-site/src/content/docs/administration/horizontal-scaling.mddocs-site/src/content/docs/administration/monitoring.mddocs-site/src/content/docs/development/architecture.mddocs-site/src/content/docs/development/contributing.mddocs-site/src/content/docs/development/database-schema.mddocs-site/src/content/docs/development/local-dev-setup.mddocs-site/src/content/docs/development/release-gates.mddocs-site/src/content/docs/features/ai-review.mddocs-site/src/content/docs/getting-started/first-repository.mddocs-site/src/content/docs/getting-started/installation.mddocs-site/src/content/docs/getting-started/quick-start.mddocs-site/src/content/docs/guides/example.mddocs-site/src/content/docs/index.mdxdocs-site/src/content/docs/reference/api-versioning.mddocs-site/src/content/docs/reference/cli-auth-config.mddocs-site/src/content/docs/reference/cli-core-commands.mddocs-site/src/content/docs/reference/example.mddocs-site/src/content/docs/tutorials/zero-to-production.mddocs-site/src/custom.cssdocs/FREE-DEPLOYMENT.mddocs/RENDER-DEPLOYMENT.mddocs/administration/air-gapped.mddocs/administration/backup-restore.mddocs/administration/configuration.mddocs/administration/deploy-nas.mddocs/administration/deployment.mddocs/administration/expose-nas.mddocs/administration/horizontal-scaling.mddocs/administration/incident-runbook.mddocs/administration/known-accepted-vulns.jsondocs/administration/monitoring.mddocs/cli.mddocs/development/contributing.mddocs/development/release-gates.mddocs/getting-started/installation.mddocs/getting-started/quick-start.mddocs/getting-started/ssh-keys.mddocs/reference/api-versioning.mddrizzle/0000_abandoned_king_bedlam.sqldrizzle/0000_black_monster_badoon.sql
▼.astro/settings.json
+1-1
1
1
{
2
2
"_variables": {
3
- "lastUpdateCheck": 1783539359887
3
+ "lastUpdateCheck": 1783961044818
4
4
}
5
5
}
6
6
▼.env.example
+10-0
76
76
STORAGE_TYPE=local
77
77
STORAGE_PATH=./data/storage
78
78
79
+# NAS / dedicated-disk deployments (docker-compose.nas.yml):
80
+# Point every data directory at your NAS mount or dedicated disk.
81
+# OCH_DATA_DIR=/mnt/nas/opencodehub
82
+# STORAGE_PATH=./data/storage # or /mnt/nas/opencodehub/storage with local driver
83
+
79
84
# S3 Configuration (required when STORAGE_TYPE=s3)
80
85
# Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS,
81
86
# Ceph RGW, Wasabi, Backblaze B2, etc.
159
164
AIR_GAPPED_MODE=false
160
165
161
166
# ── Observability (optional) ────────────────────────────────
167
+# Bearer token protecting GET /api/metrics (Prometheus). Set a value in
168
+# production; when empty, the endpoint is public and should be restricted
169
+# at the reverse proxy.
170
+METRICS_TOKEN=
171
+
162
172
# Grafana Cloud OTLP
163
173
GRAFANA_OTLP_ENDPOINT=
164
174
GRAFANA_INSTANCE_ID=
165
175
▼.github/workflows/ai-review.yml
+4-1
22
22
with:
23
23
bun-version: latest
24
24
25
+ - name: Install dependencies
26
+ run: bun install --frozen-lockfile
27
+
25
28
- name: Run AI Review
26
29
env:
27
30
# Use user's PAT to count towards their GitHub contribution graph, fallback to standard bot token
28
31
GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
29
- GEMINI_API_KEY: ${{ secrets.GEMINIKEY }}
32
+ AGENT_API_KEY: ${{ secrets.AGENT_API_KEY }}
30
33
PR_NUMBER: ${{ github.event.pull_request.number }}
31
34
REPO: ${{ github.repository }}
32
35
BASE_SHA: ${{ github.event.pull_request.base.sha }}
33
36
▼.github/workflows/ci.yml
+22-7
255
255
steps:
256
256
- uses: actions/checkout@v4
257
257
258
+ - name: Set up Docker Buildx
259
+ uses: docker/setup-buildx-action@v3
260
+
258
261
- name: Build Docker image
259
- run: docker build -t opencodehub:ci -f Dockerfile .
262
+ uses: docker/build-push-action@v5
263
+ with:
264
+ context: .
265
+ load: true
266
+ tags: opencodehub:ci
267
+ file: Dockerfile
268
+ cache-from: type=gha
269
+ cache-to: type=gha,mode=max
260
270
261
271
- name: Run Trivy vulnerability scanner
262
272
uses: aquasecurity/trivy-action@master
347
357
DATABASE_DRIVER: postgres
348
358
- name: Start server and run perf check
349
359
run: |
350
- bun run dev &
360
+ HOST=127.0.0.1 PORT=4321 node ./dist/server/entry.mjs &
351
361
SERVER_PID=$!
352
- sleep 8
362
+ sleep 5
363
+ curl -s http://127.0.0.1:4321/api/health > /dev/null || true
364
+ curl -s http://127.0.0.1:4321/api/metrics > /dev/null || true
353
365
bun run perf:baseline
354
366
kill $SERVER_PID 2>/dev/null || true
355
367
env:
360
372
INTERNAL_HOOK_SECRET: "ci-perf-hook"
361
373
CSRF_SKIP_DEV: "true"
362
374
RATE_LIMIT_SKIP_DEV: "true"
375
+ RATE_LIMIT_ENABLED: "false"
363
376
SKIP_REDIS_CHECK: "1"
364
377
PERF_BASE_URL: http://127.0.0.1:4321
365
378
PERF_PATHS: /api/health,/api/metrics
460
473
runs-on: ubuntu-latest
461
474
needs: [build, e2e]
462
475
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
476
+ permissions:
477
+ contents: read
478
+ id-token: write
463
479
steps:
464
480
- uses: actions/checkout@v4
465
481
482
498
done
483
499
echo "::error::bun install failed after 3 attempts"
484
500
exit 1
485
- working-directory: cli
486
501
487
502
- name: Build CLI
488
503
run: bun run build
500
515
quality-gate:
501
516
name: Quality Gate
502
517
runs-on: ubuntu-latest
503
- needs: [lint, docs-parity, security, test, e2e, container-scan, sast, perf, build]
518
+ needs: [lint, docs-parity, security, test, e2e, container-scan, sast, performance, build]
504
519
if: always()
505
520
steps:
506
521
- name: Check all gates
515
530
516
531
echo "Docs Parity: ${{ needs.docs-parity.result }}"
517
532
echo "Security Gates: ${{ needs.security.result }}"
518
- echo "Perf Gate: ${{ needs.perf.result }}"
533
+ echo "Perf Gate: ${{ needs.performance.result }}"
519
534
520
535
if [[ "${{ needs.lint.result }}" != "success" ]] || \
521
536
[[ "${{ needs.docs-parity.result }}" != "success" ]] || \
522
537
[[ "${{ needs.security.result }}" != "success" ]] || \
523
538
[[ "${{ needs.test.result }}" != "success" ]] || \
524
- [[ "${{ needs.perf.result }}" != "success" ]] || \
539
+ [[ "${{ needs.performance.result }}" != "success" ]] || \
525
540
[[ "${{ needs.build.result }}" != "success" ]]; then
526
541
echo "::error::Quality gate FAILED — required checks did not pass"
527
542
exit 1
528
543
▼.github/workflows/docker-publish.yml
+66-20
8
8
9
9
env:
10
10
REGISTRY: docker.io
11
- IMAGE_NAME: swadhinbiswas/opencodehub
11
+ IMAGE_NAME: opencodehub/opencodehub
12
+ ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true"
12
13
13
14
jobs:
14
15
build-and-push:
21
22
- name: Checkout
22
23
uses: actions/checkout@v4
23
24
25
+ - name: Set up QEMU
26
+ uses: docker/setup-qemu-action@v3
27
+
24
28
- name: Set up Docker Buildx
25
29
uses: docker/setup-buildx-action@v3
26
30
30
34
username: ${{ secrets.DOCKER_USERNAME }}
31
35
password: ${{ secrets.DOCKER_TOKEN }}
32
36
33
- - name: Extract metadata
34
- id: meta
37
+ - name: Extract Metadata (App)
38
+ id: meta-app
35
39
uses: docker/metadata-action@v5
36
40
with:
37
41
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42
46
type=sha,prefix=
43
47
type=raw,value=latest,enable={{is_default_branch}}
44
48
labels: |
45
- org.opencontainers.image.title=OpenCodeHub
46
- org.opencontainers.image.description=A modern, self-hosted Git platform for teams that want speed, control, and clean workflows.
49
+ org.opencontainers.image.title=OpenCodeHub Platform
50
+ org.opencontainers.image.description=A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review.
51
+ org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub
52
+ org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub
53
+ org.opencontainers.image.licenses=MIT
54
+
55
+ - name: Extract Metadata (Worker)
56
+ id: meta-worker
57
+ uses: docker/metadata-action@v5
58
+ with:
59
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-worker
60
+ tags: |
61
+ type=ref,event=branch
62
+ type=semver,pattern={{version}}
63
+ type=semver,pattern={{major}}.{{minor}}
64
+ type=sha,prefix=
65
+ type=raw,value=latest,enable={{is_default_branch}}
66
+ labels: |
67
+ org.opencontainers.image.title=OpenCodeHub Worker
68
+ org.opencontainers.image.description=Background task worker for OpenCodeHub (queues, webhooks, notifications).
47
69
org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub
48
70
org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub
49
71
org.opencontainers.image.licenses=MIT
50
72
51
- - name: Build and push app image
73
+ - name: Extract Metadata (Runner)
74
+ id: meta-runner
75
+ uses: docker/metadata-action@v5
76
+ with:
77
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner
78
+ tags: |
79
+ type=ref,event=branch
80
+ type=semver,pattern={{version}}
81
+ type=semver,pattern={{major}}.{{minor}}
82
+ type=sha,prefix=
83
+ type=raw,value=latest,enable={{is_default_branch}}
84
+ labels: |
85
+ org.opencontainers.image.title=OpenCodeHub Runner
86
+ org.opencontainers.image.description=Docker-in-Docker CI/CD runner for OpenCodeHub pipelines.
87
+ org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub
88
+ org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub
89
+ org.opencontainers.image.licenses=MIT
90
+
91
+ - name: Build and Push App Image
52
92
uses: docker/build-push-action@v5
53
93
with:
54
94
context: .
55
95
file: ./Dockerfile
56
96
push: true
57
- tags: ${{ steps.meta.outputs.tags }}
58
- labels: ${{ steps.meta.outputs.labels }}
97
+ tags: ${{ steps.meta-app.outputs.tags }}
98
+ labels: ${{ steps.meta-app.outputs.labels }}
59
99
cache-from: type=gha
60
100
cache-to: type=gha,mode=max
61
101
platforms: linux/amd64,linux/arm64
62
102
63
- - name: Build and push runner image
103
+ - name: Build and Push Worker Image
104
+ uses: docker/build-push-action@v5
105
+ with:
106
+ context: .
107
+ file: ./Dockerfile.worker
108
+ push: true
109
+ tags: ${{ steps.meta-worker.outputs.tags }}
110
+ labels: ${{ steps.meta-worker.outputs.labels }}
111
+ cache-from: type=gha
112
+ cache-to: type=gha,mode=max
113
+ platforms: linux/amd64,linux/arm64
114
+
115
+ - name: Build and Push Runner Image
64
116
uses: docker/build-push-action@v5
65
117
with:
66
118
context: .
67
119
file: ./Dockerfile.runner
68
120
push: true
69
- tags: |
70
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:latest
71
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:${{ github.sha }}
72
- labels: |
73
- org.opencontainers.image.title=OpenCodeHub Runner
74
- org.opencontainers.image.description=CI/CD runner for OpenCodeHub
75
- org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub
76
- org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub
77
- org.opencontainers.image.licenses=MIT
121
+ tags: ${{ steps.meta-runner.outputs.tags }}
122
+ labels: ${{ steps.meta-runner.outputs.labels }}
78
123
cache-from: type=gha
79
124
cache-to: type=gha,mode=max
125
+ platforms: linux/amd64,linux/arm64
80
126
81
127
- name: Update Docker Hub Repository Description
82
128
uses: peter-evans/dockerhub-description@v4
84
130
username: ${{ secrets.DOCKER_USERNAME }}
85
131
password: ${{ secrets.DOCKER_TOKEN }}
86
132
repository: ${{ env.IMAGE_NAME }}
87
- readme-filepath: ./README.md
88
- short-description: "A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review."
133
+ readme-filepath: ./DOCKERHUB.md
134
+ short-description: "Self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI code review"
89
135
▼.gitignore
+3-2
61
61
# Docker
62
62
.docker/
63
63
64
-# Drizzle
65
-drizzle/meta/
64
+# Drizzle — migrations and the migration journal must be committed so that
65
+# runtime containers can apply schema deterministically (scripts/migrate.ts)
66
+# drizzle/
66
67
67
68
# Monaco Editor cache
68
69
.monaco/
69
70
▼.mimocode/.cron-lock
+1-0
1
+{"pid":8618,"startedAt":1784441536749}
0
2
▼.mimocode/plans/1784309305424-sunny-pixel.md
+57-0
1
+# GitHub-like Profile Portfolio Page
2
+
3
+## Current State
4
+The profile page at `src/pages/[owner]/index.astro` already has:
5
+- Avatar, display name, username, bio
6
+- Details (company, location, email, website, join date)
7
+- Followers/following
8
+- Contribution graph
9
+- Repository list
10
+- Activity feed
11
+
12
+## What Needs to Change
13
+
14
+On GitHub, when you visit `github.com/username`, you see:
15
+1. **Left sidebar**: Avatar (large), name, username, bio, details, followers/following, "Edit profile" button
16
+2. **Main content**:
17
+ - "Read me" section rendered from `username/username` repository's README.md
18
+ - "Pinned" repositories section (6 repos max, customizable)
19
+ - Contribution graph
20
+ - Activity feed
21
+
22
+The current OpenCodeHub page has this mostly but the layout and "Read me" section from the special repo are missing.
23
+
24
+## Implementation Plan
25
+
26
+### 1. Auto-create profile repository on user registration
27
+- When a user registers, automatically create a `{username}/{username}` repository (like GitHub)
28
+- Seed it with a default README.md containing the user's name and bio
29
+- This repo should be private by default but its README is shown on the public profile
30
+
31
+### 2. Update profile page layout
32
+- Move to GitHub's 2-column layout: left sidebar (avatar + details) + main content
33
+- Left sidebar: Large avatar, name, username, bio, details, followers/following
34
+- Main content: Render README.md from profile repo, pinned repos, contribution graph, activity
35
+
36
+### 3. Add "Read me" section
37
+- Fetch `README.md` from `{username}/{username}` repository
38
+- Render it as markdown in the profile page (like GitHub does)
39
+- If no README exists, show the current default layout
40
+
41
+### 4. Add pinned repositories section
42
+- Allow users to pin up to 6 repositories to their profile
43
+- Store pinned repo IDs in user settings or a separate table
44
+- Show pinned repos in a grid below the README
45
+
46
+### Files to Modify
47
+- `src/pages/[owner]/index.astro` — Main profile page
48
+- `src/pages/api/repos/index.ts` — Auto-create profile repo on registration
49
+- `src/pages/register.astro` — Trigger profile repo creation
50
+- `src/components/repo/ProfileREADME.tsx` — New component to render profile README
51
+- `src/components/repo/PinnedRepos.tsx` — New component for pinned repos grid
52
+
53
+### Verification
54
+1. Register a new user → profile repo auto-created with README
55
+2. Visit `/{username}` → shows GitHub-like profile with README, pinned repos, contribution graph
56
+3. Edit profile repo README → profile page updates
57
+4. Pin/unpin repositories → grid updates
0
58
▼AGENTS.md
+5-3
294
294
npm install -g opencodehub-cli
295
295
296
296
# Auth
297
-och auth login --url http://localhost:3000
297
+och auth login --url http://localhost:4321
298
298
299
299
# Stack workflow
300
300
och stack create feature/part-1 # Create stacked branch
343
343
bun run scripts/seed-admin.ts # Create admin user
344
344
345
345
# Dev
346
-npm run dev # Start dev server (port 3000)
346
+npm run dev # Start dev server (port 4321)
347
347
npm run git:start # Start SSH git server (port 2222)
348
348
npm run worker:start # Start background worker
349
349
npm run runner:start # Start CI runner
392
392
4. Configure Redis for distributed locking
393
393
5. Enable rate limiting (`RATE_LIMIT_ENABLED=true`)
394
394
6. Set up email (SMTP) for notifications
395
-7. Configure storage backend (S3/GCS recommended for multi-node)
395
+7. Configure storage backend (`local` for single-host/NAS, S3-compatible for multi-node)
396
+8. Set `METRICS_TOKEN` to protect `GET /api/metrics`
397
+9. Apply committed migrations (`bun run migrate` — runs automatically in containers)
396
398
397
399
### Vercel/Edge
398
400
- Turso (LibSQL) recommended for edge deployment
399
401
▼DEPLOYMENT.md
+24-26
87
87
DATABASE_URL=postgresql://user:password@host:5432/opencodehub
88
88
```
89
89
90
-**Storage (Use cloud storage, not local):**
90
+**Storage (local or S3-compatible — only two backends supported):**
91
91
```bash
92
+# local (default): filesystem, ideal for a dedicated disk or NAS mount
93
+STORAGE_TYPE=local
94
+STORAGE_PATH=/mnt/nas/opencodehub/storage # point at your NAS mount / dedicated disk
95
+
96
+# or S3-compatible (AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, ...)
92
97
STORAGE_TYPE=s3
93
98
STORAGE_BUCKET=opencodehub-production
94
99
STORAGE_REGION=us-east-1
95
-S3_ACCESS_KEY=<your-access-key>
96
-S3_ACCESS_KEY=<your-access-key>
97
-S3_SECRET_KEY=<your-secret-key>
100
+STORAGE_ENDPOINT= # leave empty for AWS S3; set for other vendors
101
+STORAGE_ACCESS_KEY_ID=<your-access-key>
102
+STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
98
103
```
99
104
100
-**Google Drive + Turso Stack:**
101
-See [docs/GDRIVE_STACK.md](docs/GDRIVE_STACK.md) for detailed setup.
102
-```bash
103
-STORAGE_TYPE=gdrive
104
-GOOGLE_CLIENT_ID=...
105
-GOOGLE_CLIENT_SECRET=...
106
-GOOGLE_REFRESH_TOKEN=...
107
-GOOGLE_FOLDER_ID=...
108
-```
105
+> Only `local` and `s3` storage backends are supported. Google Drive,
106
+> OneDrive, Dropbox, FTP, GCS, Azure, and rclone-as-adapter have been
107
+> removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT`.
108
+> See `docs/guides/storage-adapters.md`.
109
109
110
110
**Optional but Recommended:**
111
111
```bash
112
-# Error Monitoring
113
-SENTRY_DSN=https://your-sentry-dsn
114
-
115
112
# Email
116
113
SMTP_HOST=smtp.example.com
117
114
SMTP_PORT=587
120
117
121
118
122
119
# Redis (for session management)
123
-REDIS_URL=redis://localhost:6379
120
+REDIS_URL=redis://:your-password@localhost:6379
124
121
```
125
122
126
123
### Deployment Steps
169
166
build: .
170
167
restart: always
171
168
ports:
172
- - "3000:3000"
169
+ - "4321:4321"
173
170
env_file: .env.production
174
171
depends_on:
175
172
- postgres
179
176
environment:
180
177
NODE_ENV: production
181
178
healthcheck:
182
- test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
179
+ test: ["CMD", "curl", "-f", "http://localhost:4321/api/health"]
183
180
interval: 30s
184
181
timeout: 10s
185
182
retries: 3
208
205
```
209
206
210
207
```bash
211
-# Deploy with Docker
212
-docker-compose -f docker-compose.prod.yml up -d
208
+# Deploy with the repository's compose files (kept in sync with the codebase)
209
+docker compose up -d # quickstart
210
+docker compose -f docker-compose.production.yml --profile production up -d # Caddy TLS
213
211
214
212
# View logs
215
-docker-compose -f docker-compose.prod.yml logs -f app
213
+docker compose logs -f app
216
214
217
215
# Create admin user in container
218
-docker-compose exec app bun run scripts/seed-admin.ts
216
+docker compose exec app bun run scripts/seed-admin.ts
219
217
```
220
218
221
219
### Reverse Proxy (Nginx)
237
235
238
236
# Proxy configuration
239
237
location / {
240
- proxy_pass http://localhost:3000;
238
+ proxy_pass http://localhost:4321;
241
239
proxy_set_header Host $host;
242
240
proxy_set_header X-Real-IP $remote_addr;
243
241
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
251
249
252
250
# Git operations (larger timeouts)
253
251
location ~ ^/[^/]+/[^/]+\.git/ {
254
- proxy_pass http://localhost:3000;
252
+ proxy_pass http://localhost:4321;
255
253
proxy_read_timeout 600s;
256
254
proxy_send_timeout 600s;
257
255
proxy_buffer_size 128k;
481
479
- **API Documentation:** https://git.yourcompany.com/api/docs
482
480
- **Admin Panel:** https://git.yourcompany.com/admin
483
481
- **User Guide:** See README.md
484
-- **Security Best Practices:** See production_analysis.md
482
+- **Security Best Practices:** See docs/administration/security.md
485
483
486
484
---
487
485
488
486
▼DOCKERHUB.md
+209-0
1
+# OpenCodeHub
2
+
3
+[![Docker Image Size](https://img.shields.io/docker/image-size/opencodehub/opencodehub/latest?style=flat-square&color=blue)](https://hub.docker.com/r/opencodehub/opencodehub)
4
+[![Docker Pulls](https://img.shields.io/docker/pulls/opencodehub/opencodehub?style=flat-square&color=emerald)](https://hub.docker.com/r/opencodehub/opencodehub)
5
+[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6
+[![Documentation](https://img.shields.io/badge/Docs-docs.opencodehub.space-indigo?style=flat-square)](https://docs.opencodehub.space)
7
+
8
+**The self-hosted Git platform built for speed, control, and modern software engineering workflows.**
9
+
10
+OpenCodeHub is an open-source alternative to GitHub/GitLab featuring native Graphite-style **Stacked PRs**, a **Merge Queue with speculative builds**, a **GitHub Actions-compatible CI/CD engine**, and multi-provider **AI Code Review**.
11
+
12
+---
13
+
14
+## Key Features
15
+
16
+- 🥞 **Stacked PRs** — Native stacked branch workflows in Web UI & CLI (`och stack create/submit/sync`)
17
+- 🚦 **Merge Queue** — Priority lanes, speculatively tested builds, and auto-merge controls
18
+- ⚡ **CI/CD Pipeline Engine** — GitHub Actions workflow runner using Docker-in-Docker execution
19
+- 🤖 **AI Code Review** — Multi-provider support (OpenAI, Anthropic, Gemini, Groq, local Ollama)
20
+- 🔒 **Enterprise Security** — Secret scanning, Trivy vulnerability scans, license compliance, OIDC SSO, 2FA/TOTP, and path permissions
21
+- 📦 **Pluggable Storage** — Local filesystem, AWS S3, Cloudflare R2, MinIO, Ceph, Garage, and any S3-compatible backend
22
+- 🗄️ **Multi-Database Support** — PostgreSQL (recommended for production), SQLite, and Turso/LibSQL
23
+- 🧰 **Developer CLI** — `opencodehub-cli` (`och`) with interactive terminal cockpit (`och focus`) and stack visualization
24
+
25
+---
26
+
27
+## Architecture Overview
28
+
29
+```
30
+┌─────────────────────────────────────────────────────────────┐
31
+│ CLIENTS │
32
+│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │
33
+└─────────────────────────────────────────────────────────────┘
34
+ │
35
+┌─────────────────────────────────────────────────────────────┐
36
+│ OPENCODEHUB PLATFORM │
37
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
38
+│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │
39
+│ │ (Astro+React)││ (140+ routes)││ (src/pages/api/) │ │
40
+│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
41
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
42
+│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │
43
+│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │
44
+│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
45
+└─────────────────────────────────────────────────────────────┘
46
+ │
47
+┌─────────────────────────────────────────────────────────────┐
48
+│ PERSISTENCE & INFRASTRUCTURE │
49
+│ PostgreSQL / SQLite │ Redis │ S3 / MinIO / Local │
50
+└─────────────────────────────────────────────────────────────┘
51
+```
52
+
53
+---
54
+
55
+## Quick Start
56
+
57
+### 1. Single Container (Quick Test)
58
+
59
+```bash
60
+docker run -d \
61
+ --name opencodehub \
62
+ -p 4321:4321 \
63
+ -p 2222:2222 \
64
+ -v opencodehub-data:/data \
65
+ -e JWT_SECRET=$(openssl rand -hex 32) \
66
+ -e SESSION_SECRET=$(openssl rand -hex 32) \
67
+ -e INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) \
68
+ -e SITE_URL=http://localhost:4321 \
69
+ opencodehub/opencodehub:latest
70
+```
71
+
72
+Open **http://localhost:4321** in your browser to get started.
73
+
74
+---
75
+
76
+### 2. Production Docker Compose Stack (Recommended)
77
+
78
+```yaml
79
+version: "3.8"
80
+
81
+services:
82
+ app:
83
+ image: opencodehub/opencodehub:latest
84
+ container_name: opencodehub-app
85
+ restart: always
86
+ ports:
87
+ - "4321:4321" # Web UI & API
88
+ - "2222:2222" # Git SSH Server
89
+ environment:
90
+ - NODE_ENV=production
91
+ - SITE_URL=https://git.yourdomain.com
92
+ - DATABASE_DRIVER=postgres
93
+ - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable
94
+ - REDIS_URL=redis://redis:6379
95
+ - JWT_SECRET=${JWT_SECRET}
96
+ - SESSION_SECRET=${SESSION_SECRET}
97
+ - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET}
98
+ - RUNNER_SECRET=${RUNNER_SECRET}
99
+ - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY}
100
+ volumes:
101
+ - app-data:/data
102
+ depends_on:
103
+ - postgres
104
+ - redis
105
+
106
+ worker:
107
+ image: opencodehub/opencodehub-worker:latest
108
+ container_name: opencodehub-worker
109
+ restart: always
110
+ environment:
111
+ - NODE_ENV=production
112
+ - DATABASE_DRIVER=postgres
113
+ - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable
114
+ - REDIS_URL=redis://redis:6379
115
+ volumes:
116
+ - app-data:/data
117
+ depends_on:
118
+ - postgres
119
+ - redis
120
+
121
+ runner:
122
+ image: opencodehub/opencodehub-runner:latest
123
+ container_name: opencodehub-runner
124
+ restart: always
125
+ privileged: true
126
+ environment:
127
+ - OPENCODEHUB_URL=http://app:4321
128
+ - RUNNER_SECRET=${RUNNER_SECRET}
129
+ volumes:
130
+ - /var/run/docker.sock:/var/run/docker.sock
131
+ depends_on:
132
+ - app
133
+
134
+ postgres:
135
+ image: postgres:16-alpine
136
+ container_name: opencodehub-postgres
137
+ restart: always
138
+ environment:
139
+ POSTGRES_USER: opencodehub
140
+ POSTGRES_PASSWORD: secretpass
141
+ POSTGRES_DB: opencodehub
142
+ volumes:
143
+ - pg-data:/var/lib/postgresql/data
144
+
145
+ redis:
146
+ image: redis:7-alpine
147
+ container_name: opencodehub-redis
148
+ restart: always
149
+ volumes:
150
+ - redis-data:/data
151
+
152
+volumes:
153
+ app-data:
154
+ pg-data:
155
+ redis-data:
156
+```
157
+
158
+Start the stack:
159
+```bash
160
+docker compose up -d
161
+docker compose exec app bun run scripts/seed-admin.ts
162
+```
163
+
164
+---
165
+
166
+## Environment Variables Reference
167
+
168
+| Variable | Required | Default | Description |
169
+|----------|----------|---------|-------------|
170
+| `SITE_URL` | Yes | `http://localhost:4321` | Base URL of your platform (e.g. `https://git.company.com`) |
171
+| `DATABASE_DRIVER` | No | `postgres` | Database driver (`postgres`, `sqlite`, `turso`) |
172
+| `DATABASE_URL` | Yes | — | Connection string (PostgreSQL/SQLite) |
173
+| `REDIS_URL` | Yes | — | Redis connection URI for sessions & locks |
174
+| `JWT_SECRET` | Yes | — | 32+ char secret for JWT token signing |
175
+| `SESSION_SECRET` | Yes | — | 32+ char secret for cookie encryption |
176
+| `INTERNAL_HOOK_SECRET` | Yes | — | Shared secret for Git hook callbacks |
177
+| `STORAGE_TYPE` | No | `local` | Storage backend (`local` or `s3`) |
178
+| `STORAGE_BUCKET` | If `STORAGE_TYPE=s3` | — | S3 bucket name |
179
+| `STORAGE_REGION` | If `STORAGE_TYPE=s3` | `us-east-1` | S3 region |
180
+| `STORAGE_ENDPOINT` | Optional | — | S3-compatible custom endpoint (MinIO / R2 / Garage) |
181
+| `STORAGE_ACCESS_KEY_ID` | If `STORAGE_TYPE=s3` | — | Access Key ID |
182
+| `STORAGE_SECRET_ACCESS_KEY` | If `STORAGE_TYPE=s3` | — | Secret Access Key |
183
+| `METRICS_TOKEN` | Optional | — | Bearer token for `GET /api/metrics` |
184
+
185
+---
186
+
187
+## OpenCodeHub CLI (`och`)
188
+
189
+Install the official CLI to manage stacks, merge queues, and reviews:
190
+
191
+```bash
192
+npm install -g opencodehub-cli
193
+
194
+# Authenticate with your self-hosted instance
195
+och auth login --url https://git.yourdomain.com
196
+
197
+# Create stacked PR branches
198
+och stack create feature/part-1
199
+och stack submit
200
+```
201
+
202
+---
203
+
204
+## Useful Links & Community
205
+
206
+- **Official Repository**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub)
207
+- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space)
208
+- **Bug Tracker**: [github.com/swadhinbiswas/OpencodeHub/issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
209
+- **License**: MIT
0
210
▼Dockerfile
+29-11
6
6
org.opencontainers.image.source="https://github.com/swadhinbiswas/OpencodeHub" \
7
7
org.opencontainers.image.licenses="MIT"
8
8
9
-# Install dependencies
9
+# Install ALL dependencies (needed for build including CSS tooling)
10
10
FROM base AS deps
11
11
RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/*
12
12
COPY package.json bun.lock ./
16
16
(echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \
17
17
done
18
18
19
+# Install production dependencies only (for runtime image)
20
+FROM base AS prod-deps
21
+RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/*
22
+COPY package.json bun.lock ./
23
+COPY cli/package.json ./cli/package.json
24
+# Clear Bun install cache to avoid stale/corrupt node-gyp entries across layers
25
+RUN rm -rf /root/.bun/install/cache || true
26
+RUN for i in 1 2 3; do \
27
+ bun install --frozen-lockfile --production && break || \
28
+ (echo "prod bun install attempt $i failed, clearing cache and retrying in 10s..." && \
29
+ rm -rf /root/.bun/install/cache && sleep 10); \
30
+ done
31
+
19
32
# Build the application
20
33
FROM base AS builder
21
34
COPY --from=deps /app/node_modules ./node_modules
24
37
RUN bun run build
25
38
26
39
# Production image
27
-FROM oven/bun:1 AS runner
40
+FROM oven/bun:1-slim AS runner
28
41
WORKDIR /app
29
42
30
-# Install git, ssh, and bash (needed for git operations and entrypoint)
31
-RUN apt-get update && apt-get install -y git openssh-client bash && rm -rf /var/lib/apt/lists/*
43
+# Install git, ssh, bash, and wget (git operations, entrypoint, healthcheck)
44
+RUN apt-get update && apt-get install -y --no-install-recommends git openssh-client bash wget && \
45
+ rm -rf /var/lib/apt/lists/*
32
46
33
47
# Create data directories
34
-RUN mkdir -p /data/repos /data/storage /data/cache /data/ssh && \
48
+RUN mkdir -p /data/repositories /data/storage /data/cache /data/ssh && \
35
49
chown -R bun:bun /data
36
50
37
-# Copy built application
51
+# Copy only what's needed for runtime
38
52
COPY --from=builder --chown=bun:bun /app/dist ./dist
39
-COPY --from=builder --chown=bun:bun /app/node_modules ./node_modules
53
+COPY --from=prod-deps --chown=bun:bun /app/node_modules ./node_modules
40
54
COPY --from=builder --chown=bun:bun /app/package.json ./
41
55
42
-# Copy drizzle config and schema for migrations
56
+# Copy drizzle config, committed migrations, and full source tree.
57
+# The full src/ is required so raw-TS entrypoints (scripts/ssh-server.ts,
58
+# scripts/worker.ts) can resolve the "@/..." path aliases via tsconfig.
43
59
COPY --from=builder --chown=bun:bun /app/drizzle.config.ts ./
44
-COPY --from=builder --chown=bun:bun /app/src/db ./src/db
60
+COPY --from=builder --chown=bun:bun /app/drizzle ./drizzle
61
+COPY --from=builder --chown=bun:bun /app/src ./src
45
62
COPY --from=builder --chown=bun:bun /app/tsconfig.json ./
46
63
47
-# Copy entrypoint script
64
+# Copy runtime scripts (migrate, ssh-server, worker) and entrypoint
65
+COPY --from=builder --chown=bun:bun /app/scripts ./scripts
48
66
COPY --chown=bun:bun docker-entrypoint.sh ./
49
67
50
68
# Set environment variables
51
69
ENV HOST=0.0.0.0
52
70
ENV PORT=4321
53
71
ENV DATA_DIR=/data
54
-ENV REPOS_PATH=/data/repos
72
+ENV GIT_REPOS_PATH=/data/repositories
55
73
ENV STORAGE_PATH=/data/storage
56
74
ENV CACHE_PATH=/data/cache
57
75
ENV SSH_PATH=/data/ssh
58
76
▼Dockerfile.worker
+44-0
1
+FROM oven/bun:1 AS base
2
+WORKDIR /app
3
+
4
+# Install dependencies
5
+FROM base AS deps
6
+RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/*
7
+COPY package.json bun.lock ./
8
+COPY cli/package.json ./cli/package.json
9
+RUN for i in 1 2 3; do \
10
+ bun install --frozen-lockfile && break || \
11
+ (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \
12
+ done
13
+
14
+# Production image
15
+FROM oven/bun:1 AS runner
16
+WORKDIR /app
17
+
18
+# Install git (needed for git operations)
19
+RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
20
+
21
+# Create data directories
22
+RUN mkdir -p /data/repositories /data/storage /data/cache && \
23
+ chown -R bun:bun /data
24
+
25
+# Copy node_modules and source
26
+COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules
27
+COPY --from=deps --chown=bun:bun /app/package.json ./
28
+COPY --from=deps --chown=bun:bun /app/cli ./cli
29
+COPY --from=deps --chown=bun:bun /app/tsconfig.json ./
30
+
31
+# Copy source files needed for worker
32
+COPY --from=deps --chown=bun:bun /app/src/lib ./src/lib
33
+COPY --from=deps --chown=bun:bun /app/src/db ./src/db
34
+COPY --from=deps --chown=bun:bun /app/scripts/worker.ts ./scripts/worker.ts
35
+
36
+# Set environment variables
37
+ENV NODE_ENV=production
38
+ENV DATA_DIR=/data
39
+
40
+# Switch to non-root user
41
+USER bun
42
+
43
+# Start the worker
44
+CMD ["bun", "scripts/worker.ts"]
0
45
▼PRODUCTION_READINESS.md
+32-14
1
1
# OpenCodeHub Production Readiness Report
2
2
3
-**Date:** 2026-04-30
3
+**Date:** 2026-08-02
4
4
**Auditor:** Agent Code Review
5
5
**Status:** Production-ready
6
6
11
11
OpenCodeHub is a self-hosted Git platform with enterprise-grade features including Git hosting (HTTP/SSH), PRs, issues, stacked PRs, merge queue with speculative builds, CI/CD pipeline engine, and a comprehensive CLI.
12
12
13
13
**Current State:**
14
-- Lint: 0 errors, 0 warnings, 491 hints
14
+- Lint: 0 errors, 0 warnings
15
15
- Typecheck: 0 errors
16
-- Tests: 546 passing, 0 failing (114 test files, 100% pass rate)
17
-- Security: Rate limiting, CSRF, JWT, env validation, secret encryption all implemented
18
-- Docker: Full docker-compose setup with PostgreSQL, Redis, and CI runner
16
+- Tests: 546 passing, 0 failing (100% pass rate)
17
+- Security: Rate limiting, CSRF, JWT, env validation, secret encryption, authenticated metrics all implemented
18
+- Docker: Production-safe compose stacks (no default secrets, migrations applied at boot, worker + SSH services included)
19
+- Storage: `local` (filesystem/NAS) and `s3` (any S3-compatible store) only
20
+- Migrations: Versioned `drizzle/` folder committed and applied deterministically at container boot
21
+
22
+## Recent Production Hardening
23
+
24
+| Area | Change |
25
+|------|--------|
26
+| Secrets | `.env.backup-*` removed from git tracking; docker-compose no longer ships default secrets |
27
+| Migrations | Committed `drizzle/0000_*.sql`; applied at boot via `scripts/migrate.ts` (no runtime `generate`) |
28
+| `npm start` | Now runs the built server (`node ./dist/server/entry.mjs`) instead of `astro dev` |
29
+| Docker | `ssh-git` and `worker` services now run the correct process (`PROCESS_TYPE`); image copies `scripts/` + `drizzle/` |
30
+| Metrics | `GET /api/metrics` protected by optional `METRICS_TOKEN` bearer auth |
31
+| Env vars | Standardized on `DATABASE_DRIVER`, `GIT_REPOS_PATH`, `GIT_SSH_HOST_KEY`, `STORAGE_PATH` across all compose files |
32
+| Storage docs | Removed all references to removed backends (GDrive/Azure/GCS/etc.); only `local` + `s3` documented |
19
33
20
34
---
21
35
90
104
DATABASE_URL=postgresql://user:pass@localhost:5432/opencodehub
91
105
92
106
# Redis (required for distributed rate limiting, sessions, and queues)
93
-REDIS_URL=redis://localhost:6379
94
-
95
-# Storage (use S3/GCS/Azure in production, not local)
96
-STORAGE_DRIVER=s3
97
-S3_BUCKET=your-bucket
98
-S3_REGION=us-east-1
99
-S3_ACCESS_KEY=
100
-S3_SECRET_KEY=
107
+REDIS_URL=redis://:password@localhost:6379
108
+
109
+# Storage — only local and s3 are supported (local is ideal for a NAS/dedicated disk)
110
+STORAGE_TYPE=local
111
+STORAGE_PATH=/mnt/nas/opencodehub/storage
112
+
113
+# S3-compatible (AWS S3, MinIO, R2, Garage, ...) when STORAGE_TYPE=s3:
114
+STORAGE_BUCKET=your-bucket
115
+STORAGE_REGION=us-east-1
116
+STORAGE_ENDPOINT=
117
+STORAGE_ACCESS_KEY_ID=
118
+STORAGE_SECRET_ACCESS_KEY=
101
119
```
102
120
103
121
### Docker Compose (Recommended)
128
146
129
147
1. **Modular Monolith** — Easy to deploy as single container or scale individual workers
130
148
2. **Database Flexibility** — PostgreSQL for production, SQLite/Turso for dev/edge
131
-3. **Storage Abstraction** — 8+ backends via adapter pattern (S3, GCS, Azure, Google Drive, OneDrive, Dropbox, FTP, rclone)
149
+3. **Storage Abstraction** — two backends via adapter pattern: `local` (filesystem; ideal for a dedicated disk or NAS mount) and `s3` (any S3-compatible store)
132
150
4. **Git Protocol Integration** — Native HTTP + SSH via `git` CLI and `ssh2`
133
151
5. **CI/CD Engine** — GitHub Actions-compatible with Docker-based execution
134
152
6. **Stacked PRs** — Graphite-style workflows in web + CLI
135
153
▼README.md
+132-372
1
-# OpenCodeHub
2
-
3
1
<p align="center">
4
2
<picture>
5
3
<source media="(prefers-color-scheme: dark)" srcset="public/logo-dark.png">
6
4
<source media="(prefers-color-scheme: light)" srcset="public/logo-light.png">
7
- <img src="public/logo-light.png" alt="OpenCodeHub Logo" width="420" />
5
+ <img src="public/logo-light.png" alt="OpenCodeHub" width="420" />
8
6
</picture>
9
7
</p>
10
8
11
-<p align="center">
12
- <strong>A modern, self-hosted Git platform for teams that want speed, control, and clean workflows.</strong>
13
-</p>
9
+<h3 align="center">The self-hosted Git platform that doesn't compromise.</h3>
14
10
15
11
<p align="center">
16
- <a href="https://www.npmjs.com/package/opencodehub-cli"><img src="https://img.shields.io/npm/v/opencodehub-cli?style=flat-square&label=CLI" alt="CLI version"></a>
17
- <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License"></a>
18
- <a href="docs/index.md"><img src="https://img.shields.io/badge/docs-available-success?style=flat-square" alt="Docs"></a>
19
- <a href="https://github.com/swadhinbiswas/OpencodeHub/actions"><img src="https://img.shields.io/badge/CI-passing-success?style=flat-square" alt="CI"></a>
12
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/actions"><img src="https://img.shields.io/badge/CI-passing-brightgreen?style=for-the-badge" alt="CI"></a>
13
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=for-the-badge" alt="License"></a>
14
+ <a href="https://www.npmjs.com/package/opencodehub-cli"><img src="https://img.shields.io/npm/v/opencodehub-cli?style=for-the-badge&label=CLI" alt="CLI version"></a>
15
+ <a href="https://docs.opencodehub.space"><img src="https://img.shields.io/badge/docs-opencodehub.space-brightgreen?style=for-the-badge" alt="Docs"></a>
16
+ <a href="https://hub.docker.com/r/opencodehub/opencodehub"><img src="https://img.shields.io/badge/docker-opencodehub%2Fopencodehub-blue?style=for-the-badge&logo=docker" alt="Docker"></a>
20
17
</p>
21
18
22
19
---
23
20
24
-## Table of Contents
21
+OpenCodeHub is a self-hosted Git platform with **stacked PRs**, **merge queue**, **CI/CD pipelines**, and **AI code review**. One platform for everything your team needs — no vendor lock-in, no per-seat pricing.
25
22
26
-- [Why OpenCodeHub](#why-opencodehub)
27
-- [Features](#features)
28
-- [Quick Start](#quick-start)
29
-- [Architecture](#architecture)
30
-- [Documentation](#documentation)
31
-- [CLI](#cli)
32
-- [API](#api)
33
-- [Security](#security)
34
-- [Deployment](#deployment)
35
-- [Contributing](#contributing)
36
-- [License](#license)
23
+**[Documentation](https://docs.opencodehub.space)** · **[Deploy in 5 minutes](#deploy)** · **[CLI Reference](https://docs.opencodehub.space/reference/cli-commands/)**
37
24
38
25
---
39
26
40
-## Why OpenCodeHub
27
+## Demo
28
+
29
+<!-- Replace VIDEO_ID with your YouTube video ID -->
30
+[![OpenCodeHub Demo](https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg)](https://youtu.be/VIDEO_ID)
41
31
42
-Most teams eventually hit the same pain points:
32
+*Watch the full walkthrough — deployment, stacked PRs, AI review, and merge queue in action.*
43
33
44
-- **PRs are too large to review quickly** — Stacked PRs let you break changes into reviewable pieces
45
-- **Tooling is split across many services** — One platform for Git, PRs, CI/CD, issues, and wiki
46
-- **Data residency and security requirements** — Self-hosted by default, you control everything
47
-- **Merge queue complexity** — Stack-aware merge queue with speculative builds
48
-- **Slow review cycles** — AI-powered code review catches issues before human review
34
+---
35
+
36
+## Why OpenCodeHub?
49
37
50
-OpenCodeHub is built for that reality.
38
+| Problem | Solution |
39
+|---------|----------|
40
+| PRs too large to review | **Stacked PRs** — break changes into small, dependent branches |
41
+| Merge conflicts on main | **Merge Queue** — stack-aware ordering with speculative CI builds |
42
+| Slow review cycles | **AI Code Review** — catches bugs before humans even look |
43
+| Split across 5+ services | **All-in-one** — Git, PRs, CI/CD, issues, wiki in one place |
44
+| Data leaves your servers | **Self-hosted** — your code stays on your hardware |
51
45
52
46
---
53
47
54
48
## Features
55
49
56
50
### Core Platform
57
-
58
-| Feature | Status | Description |
59
-|---------|--------|-------------|
60
-| **Git Hosting** | Ready | HTTP (Smart Protocol) + SSH git push/pull |
61
-| **Pull Requests** | Ready | Comments, reviews, drafts, approvals, suggested changes |
62
-| **Issues** | Ready | Labels, milestones, project boards |
63
-| **Wiki** | Ready | Repository wiki with revision history |
64
-| **Organizations** | Ready | Teams, collaborators, repository settings |
65
-| **Branch Protection** | Ready | Required reviews, status checks, push restrictions |
51
+- **Git Hosting** — HTTP smart protocol + SSH push/pull, forks, mirroring, LFS
52
+- **Pull Requests** — Inline comments, approvals, suggested changes, draft PRs
53
+- **Issues & Projects** — Labels, milestones, custom fields, kanban boards
54
+- **Wiki** — Repository wiki with revision history
55
+- **Organizations** — Teams, collaborators, role-based access control
66
56
67
57
### Delivery Workflows
68
-
69
-| Feature | Status | Description |
70
-|---------|--------|-------------|
71
-| **Stacked PRs** | Ready | Graphite-style stack workflows in web + CLI |
72
-| **Merge Queue** | Ready | Stack-aware queue with speculative builds and priority lanes |
73
-| **CI/CD Pipelines** | Ready | GitHub Actions-compatible engine + Docker-based runner |
74
-| **Webhooks** | Ready | Outbound webhooks with event filtering |
75
-| **Automations** | Ready | Workflow automation rules |
76
-
77
-### Security & Governance
78
-
79
-| Feature | Status | Description |
80
-|---------|--------|-------------|
81
-| **Authentication** | Ready | JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP |
82
-| **Authorization** | Ready | RBAC with roles, team permissions, collaborator levels |
83
-| **Rate Limiting** | Ready | Redis-backed per-endpoint rate limiting |
84
-| **CSRF Protection** | Ready | Double-submit cookie pattern |
85
-| **Secret Scanning** | Ready | Detect secrets in commits |
86
-| **Audit Logging** | Ready | Track all administrative actions |
58
+- **Stacked PRs** — Graphite-style stacked branches with web + CLI support
59
+- **Merge Queue** — Stack-aware queue with speculative builds and priority lanes
60
+- **CI/CD Pipelines** — GitHub Actions-compatible engine with Docker-based runners
61
+- **Webhooks** — Outbound webhooks with event filtering and HMAC signing
62
+- **Automations** — Rule-based workflow automation for PRs and deployments
63
+
64
+### AI & Quality
65
+- **AI Code Review** — 10+ providers: GPT-4, Claude, Gemini, Groq, Ollama, OpenRouter
66
+- **Secret Scanning** — Detect secrets in commits before they reach production
67
+- **Branch Protection** — Required reviews, status checks, push restrictions
68
+- **Developer Metrics** — PR velocity, review efficiency, time-to-merge tracking
69
+
70
+### Security
71
+- **Authentication** — JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP, SSO/SAML
72
+- **Authorization** — RBAC with roles, team permissions, collaborator levels
73
+- **Rate Limiting** — Redis-backed per-endpoint rate limiting
74
+- **Audit Logging** — Track all administrative actions
87
75
88
76
### Extensibility
89
-
90
-| Feature | Status | Description |
91
-|---------|--------|-------------|
92
-| **REST API** | Ready | 140+ API routes |
93
-| **GraphQL** | Ready | Full GraphQL endpoint |
94
-| **OpenAPI** | Ready | Auto-generated OpenAPI spec |
95
-| **CLI** | Ready | 20+ command groups (`opencodehub-cli`) |
96
-| **Storage Adapters** | Ready | 8+ backends (S3, GCS, Azure, Google Drive, etc.) |
77
+- **REST API** — 175+ endpoints covering all platform features
78
+- **GraphQL** — Full GraphQL endpoint for flexible queries
79
+- **CLI** — `och` command line tool with 20+ command groups
80
+- **Storage** — Local, S3, MinIO, R2, or any S3-compatible backend
97
81
98
82
---
99
83
100
-## Quick Start
101
-
102
-### Prerequisites
84
+## Deploy
103
85
104
-- Node.js >= 20
105
-- npm or bun
106
-- git
107
-- PostgreSQL 14+ (recommended) or SQLite (dev)
108
-
109
-### Docker (Fastest)
86
+### Docker (Recommended)
110
87
111
88
```bash
112
-# 1. Clone
113
89
git clone https://github.com/swadhinbiswas/OpencodeHub.git
114
90
cd OpenCodeHub
115
-
116
-# 2. Configure
117
91
cp .env.example .env
118
-# Edit .env with your settings
119
-
120
-# 3. Start
121
-docker-compose up -d
122
-
123
-# 4. Create admin user
124
-docker-compose exec app bun run scripts/seed-admin.ts
125
-
126
-# 5. Open
127
-open http://localhost:4321
92
+docker compose up -d
93
+docker compose exec app bun run scripts/seed-admin.ts
128
94
```
129
95
130
-### NAS & Edge Deployment (Cloudflare / Tailscale)
96
+Open **http://localhost:4321** and create your admin account.
131
97
132
-Deploying on a home server or NAS? Use the provided `docker-compose.nas.yml` which includes `cloudflared` and `tailscale` configurations for seamless zero-trust exposure without port forwarding.
98
+### Render (Free)
133
99
134
-```bash
135
-# Expose via Cloudflare Tunnel
136
-TUNNEL_TOKEN=your_token docker-compose -f docker-compose.nas.yml --profile cloudflare up -d
137
-
138
-# OR Expose via Tailscale
139
-TS_AUTHKEY=your_key docker-compose -f docker-compose.nas.yml --profile tailscale up -d
140
-```
141
-See the [NAS Deployment Guide](docs/administration/deploy-nas.md) and [Exposing NAS Guide](docs/administration/expose-nas.md) for full details.
142
-
143
-
144
-### Manual Setup
100
+Deploy to Render with free PostgreSQL + Upstash Redis:
145
101
146
102
```bash
147
-# 1. Install dependencies
148
-npm install
149
-
150
-# 2. Configure environment
151
-cp .env.example .env
152
-# Edit .env (SQLite defaults work for dev)
153
-
154
-# 3. Initialize database
155
-npm run db:push
156
-
157
-# 4. Create admin user
158
-bun run scripts/seed-admin.ts
159
-
160
-# 5. Start development server
161
-npm run dev
103
+# 1. Create free Redis at upstash.com (Singapore region)
104
+# 2. Push to GitHub
105
+# 3. Render → New → Blueprint → Select your repo
106
+# 4. Set REDIS_URL and SITE_URL
107
+# 5. Deploy
162
108
```
163
109
164
-App URL: `http://localhost:3000` (dev) or `http://localhost:4321` (Docker)
110
+See the [Render Deployment Guide](docs/RENDER-DEPLOYMENT.md) for step-by-step instructions.
165
111
112
+### More Deployment Options
166
113
167
-## Architecture
168
-
169
-```
170
-Clients (Browser / Git CLI / och CLI)
171
- |
172
- v
173
-OpenCodeHub Platform (Astro SSR + React)
174
- |-- Web UI
175
- |-- REST API (140+ routes)
176
- |-- GraphQL Endpoint
177
- |-- Git HTTP Server
178
- |-- SSH Git Server (port 2222)
179
- |
180
- v
181
-Persistence Layer
182
- |-- PostgreSQL / SQLite / Turso (Drizzle ORM)
183
- |-- Redis (sessions, queues, caching)
184
- |-- Pluggable Storage (S3, GCS, Azure, local, etc.)
185
-```
186
-
187
-### Runtime Processes
188
-
189
-| Process | Command | Purpose |
190
-|---------|---------|---------|
191
-| Main App | `npm run dev` | Web UI + API + Git HTTP |
192
-| SSH Git | `npm run git:start` | SSH git push/pull |
193
-| Worker | `npm run worker:start` | Background jobs |
194
-| Runner | `npm run runner:start` | CI/CD pipeline execution |
195
-
196
-### Tech Stack
197
-
198
-| Layer | Technology |
199
-|-------|-----------|
200
-| Framework | Astro 4.x (SSR, Node standalone) |
201
-| UI | React 18 + Tailwind CSS + Radix UI |
202
-| Database | Drizzle ORM (PostgreSQL, SQLite, Turso) |
203
-| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) |
204
-| Git | Native git CLI + simple-git + isomorphic-git |
205
-| SSH | ssh2 library |
206
-| CI/CD | Dockerode (Docker API) |
207
-| Storage | Abstract adapter pattern (8+ backends) |
208
-| Queue | BullMQ + Redis |
209
-| CLI | Commander.js + Inquirer + simple-git |
210
-
211
-
212
-## Documentation
213
-
214
-Comprehensive documentation is available in the `docs/` directory:
215
-
216
-- **[Getting Started](docs/getting-started/installation.md)** — Installation and setup
217
-- **[Features](docs/features/)** — Stacked PRs, AI review, merge queue, CI/CD
218
-- **[Guides](docs/guides/)** — Team workflows, branch protection, webhooks
219
-- **[API Reference](docs/api/rest-api.md)** — REST API documentation
220
-- **[Administration](docs/administration/)** — Deployment, security, monitoring
221
-- **[Development](docs/development/)** — Architecture, contributing, testing
222
-- **[Tutorials](docs/tutorials/)** — Hands-on guides
114
+| Platform | Guide | Cost |
115
+|----------|-------|------|
116
+| Docker Compose | [Deployment Guide](docs/administration/deployment.md) | Free |
117
+| Render (Asia) | [Render Guide](docs/RENDER-DEPLOYMENT.md) | Free |
118
+| Oracle Cloud | [Free Deployment](docs/FREE-DEPLOYMENT.md) | Free forever |
119
+| NAS (Synology/TrueNAS) | [NAS Guide](docs/administration/deploy-nas.md) | Free |
120
+| Kubernetes | [K8s Guide](docs/administration/kubernetes.md) | Free |
121
+| Cloudflare Tunnel | [Cloudflare Guide](docs/administration/deploy-cloudflare.md) | Free |
223
122
224
123
---
225
124
226
125
## CLI
227
126
228
-The companion CLI is published as `opencodehub-cli`:
229
-
230
127
```bash
231
128
# Install
232
129
npm install -g opencodehub-cli
233
130
234
-# Authenticate
131
+# Login
235
132
och auth login --url http://localhost:4321
236
133
237
-# Stack workflow
134
+# Stacked PR workflow
238
135
och stack create feature/auth-step-1
239
136
och stack submit
240
137
och stack sync
249
144
och focus
250
145
```
251
146
252
-[Full CLI Reference](docs/reference/cli-commands.md)
147
+[Full CLI Reference](https://docs.opencodehub.space/reference/cli-commands/)
253
148
254
149
---
255
150
256
151
## API
257
152
258
-OpenCodeHub provides multiple API interfaces:
259
-
260
-### REST API
261
-
262
-140+ REST endpoints covering all platform features:
263
-
264
153
```bash
265
-# Authentication
266
-curl -X POST http://localhost:4321/api/auth/login \
267
- -H "Content-Type: application/json" \
268
- -d '{"login":"alice","password":"secret"}'
269
-
270
-# Create repository
154
+# Create a repository
271
155
curl -X POST http://localhost:4321/api/repos \
272
- -H "Authorization: Bearer <token>" \
156
+ -H "Authorization: Bearer YOUR_TOKEN" \
273
157
-H "Content-Type: application/json" \
274
158
-d '{"name":"my-project","visibility":"public"}'
275
159
276
160
# List pull requests
277
-curl http://localhost:4321/api/repos/alice/my-project/pulls \
278
- -H "Authorization: Bearer <token>"
279
-```
161
+curl http://localhost:4321/api/repos/owner/repo/pulls \
162
+ -H "Authorization: Bearer YOUR_TOKEN"
280
163
281
-### GraphQL
282
-
283
-```bash
164
+# GraphQL
284
165
curl -X POST http://localhost:4321/api/graphql \
285
- -H "Authorization: Bearer <token>" \
166
+ -H "Authorization: Bearer YOUR_TOKEN" \
286
167
-H "Content-Type: application/json" \
287
168
-d '{"query": "{ repositories { name owner { username } } }"}'
288
169
```
289
170
290
-### OpenAPI Spec
291
-
292
-The OpenAPI spec is available at:
293
-```
294
-GET /api/openapi.json
295
-```
171
+[Full API Reference](https://docs.opencodehub.space/api/rest-api/)
296
172
297
173
---
298
174
299
-## Security
175
+## Tech Stack
300
176
301
-OpenCodeHub implements comprehensive security controls:
302
-
303
-- **Authentication**: JWT sessions with secure cookies, OAuth 2.0, 2FA/TOTP
304
-- **Authorization**: Role-based access control (RBAC), repository permissions, team permissions
305
-- **Data Protection**: AES-256-GCM encryption for workflow secrets and AI config
306
-- **Transport**: HTTPS enforcement in production, TLS 1.2+
307
-- **CSRF**: Double-submit cookie pattern for state-changing requests
308
-- **Rate Limiting**: Redis-backed per-endpoint rate limiting
309
-- **Input Validation**: Zod schema validation on all API inputs
310
-- **Secret Scanning**: Detect and block secrets in commits
311
-- **SSRF Protection**: Validate webhook URLs against private IP ranges
312
-
313
-See [Security Best Practices](docs/administration/security.md) for detailed configuration.
177
+| Layer | Technology |
178
+|-------|-----------|
179
+| Framework | Astro 4.x (SSR) + React 18 |
180
+| UI | Tailwind CSS + Radix UI |
181
+| Database | PostgreSQL / SQLite / Turso (Drizzle ORM) |
182
+| Auth | JWT + OAuth + 2FA/TOTP + SSO/SAML |
183
+| Git | Native git CLI + simple-git + isomorphic-git |
184
+| SSH | ssh2 library |
185
+| CI/CD | Docker-based runners, GitHub Actions syntax |
186
+| Storage | Local filesystem or S3-compatible (AWS, MinIO, R2) |
187
+| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter |
188
+| Queue | BullMQ + Redis |
189
+| CLI | Commander.js + Inquirer |
314
190
315
191
---
316
192
317
-## Deployment
318
-
319
-### Docker Compose (Recommended)
320
-
321
-The included `docker-compose.yml` spins up:
322
-- OpenCodeHub app (port 4321)
323
-- PostgreSQL database
324
-- Redis cache
325
-- CI runner (Docker-in-Docker)
326
-- Optional: MinIO for S3-compatible storage
193
+## Architecture
327
194
328
-```bash
329
-docker-compose up -d
330
195
```
331
-
332
-### Production Checklist
333
-
334
-Before deploying to production:
335
-
336
-1. **Change ALL secrets**:
337
- ```bash
338
- JWT_SECRET=$(openssl rand -hex 32)
339
- SESSION_SECRET=$(openssl rand -hex 32)
340
- INTERNAL_HOOK_SECRET=$(openssl rand -hex 32)
341
- CRON_SECRET=$(openssl rand -hex 32)
342
- RUNNER_SECRET=$(openssl rand -hex 32)
343
- WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32)
344
- ```
345
-
346
-2. **Use PostgreSQL** (not SQLite)
347
-3. **Configure HTTPS** with reverse proxy (Nginx/Caddy)
348
-4. **Set up Redis** for distributed sessions and rate limiting
349
-5. **Configure external storage** (S3/GCS/Azure instead of local)
350
-6. **Enable rate limiting**: `RATE_LIMIT_ENABLED=true`
351
-7. **Set up SMTP** for email notifications
352
-8. **Configure backups** for PostgreSQL data
353
-
354
-See [Deployment Guide](docs/administration/deployment.md) for detailed instructions.
355
-
356
-
357
-## Database
358
-
359
-OpenCodeHub uses Drizzle ORM with support for multiple database engines:
360
-
361
-| Engine | Driver | Use Case |
362
-|--------|--------|----------|
363
-| **PostgreSQL** | `pg` | Production (recommended) |
364
-| **SQLite** | `better-sqlite3` | Development, small deployments |
365
-| **Turso/LibSQL** | `@libsql/client` | Edge deployments, Vercel |
366
-
367
-```bash
368
-# Generate migrations
369
-npm run db:generate
370
-
371
-# Apply migrations
372
-npm run db:migrate
373
-
374
-# Push schema (dev)
375
-npm run db:push
376
-
377
-# Open Drizzle Studio
378
-npm run db:studio
196
+┌─────────────────────────────────────────────────────────────┐
197
+│ CLIENTS │
198
+│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │
199
+└─────────────────────────────────────────────────────────────┘
200
+ │
201
+┌─────────────────────────────────────────────────────────────┐
202
+│ OPENCODEHUB PLATFORM │
203
+│ Web UI (Astro+React) │ REST API (175+ routes) │ GraphQL │
204
+│ Git Server (HTTP) │ SSH Server (ssh2) │ │
205
+│ Pipeline Runner (Docker) │ │
206
+└─────────────────────────────────────────────────────────────┘
207
+ │
208
+┌─────────────────────────────────────────────────────────────┐
209
+│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │
210
+└─────────────────────────────────────────────────────────────┘
379
211
```
380
212
381
213
---
382
214
383
-## Storage
384
-
385
-All blob storage goes through an abstract `StorageAdapter`:
386
-
387
-| Backend | Adapter | Use Case |
388
-|---------|---------|----------|
389
-| **Local** | `LocalStorageAdapter` | Development, single-node |
390
-| **S3** | `S3StorageAdapter` | Production (AWS, MinIO, R2) |
391
-| **Google Cloud** | `GCSStorageAdapter` | GCP deployments |
392
-| **Azure Blob** | `AzureStorageAdapter` | Azure deployments |
393
-| **Google Drive** | `GoogleDriveStorageAdapter` | Personal/small team |
394
-| **OneDrive** | `OneDriveStorageAdapter` | Microsoft ecosystem |
395
-| **Dropbox** | `DropboxStorageAdapter` | Simple cloud storage |
396
-| **Rclone** | `RcloneStorageAdapter` | Any rclone-compatible remote |
215
+## Documentation
397
216
398
-Configure via `STORAGE_DRIVER` environment variable.
217
+| Topic | Link |
218
+|-------|------|
219
+| Installation | [docs.opencodehub.space/getting-started/installation](https://docs.opencodehub.space/getting-started/installation/) |
220
+| Configuration | [docs.opencodehub.space/administration/configuration](https://docs.opencodehub.space/administration/configuration/) |
221
+| Stacked PRs | [docs.opencodehub.space/features/stacked-prs](https://docs.opencodehub.space/features/stacked-prs/) |
222
+| AI Code Review | [docs.opencodehub.space/features/ai-review](https://docs.opencodehub.space/features/ai-review/) |
223
+| CLI Reference | [docs.opencodehub.space/reference/cli-commands](https://docs.opencodehub.space/reference/cli-commands/) |
224
+| API Reference | [docs.opencodehub.space/api/rest-api](https://docs.opencodehub.space/api/rest-api/) |
225
+| Deployment | [docs.opencodehub.space/administration/deployment](https://docs.opencodehub.space/administration/deployment/) |
399
226
400
227
---
401
228
402
-## Development
229
+## Contributing
403
230
404
231
```bash
405
-# Setup
232
+# Clone and setup
233
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
234
+cd OpenCodeHub
406
235
cp .env.example .env
407
236
npm install
408
237
npm run db:push
409
238
bun run scripts/seed-admin.ts
410
239
411
-# Dev server
412
-npm run dev # Web UI + API (port 3000)
413
-npm run git:start # SSH git server (port 2222)
414
-npm run worker:start # Background worker
415
-npm run runner:start # CI runner
240
+# Start development
241
+npm run dev
416
242
417
-# Quality
418
-npm run lint
419
-npm run typecheck
243
+# Run tests
420
244
npm run test
421
-npm run test:coverage
422
-
423
-# Database
424
-npm run db:generate
425
-npm run db:migrate
426
-npm run db:push
427
-npm run db:studio
428
-```
429
-
430
-See [Contributing Guide](CONTRIBUTING.md) and [Development Docs](docs/development/).
431
-
432
-
433
-## Project Structure
434
-
435
-```
436
-OpenCodeHub/
437
-├── src/ # Main application
438
-│ ├── pages/ # Astro routes (UI + API)
439
-│ ├── lib/ # Core business logic (120+ modules)
440
-│ ├── db/ # Database schema and connection
441
-│ ├── components/ # React components
442
-│ ├── middleware.ts # Global middleware
443
-│ └── runner/ # CI runner
444
-├── cli/ # CLI package (opencodehub-cli)
445
-│ └── src/commands/ # CLI command groups
446
-├── packages/ # Additional packages
447
-│ ├── git-rpc-daemon/
448
-│ ├── merge-queue-daemon/
449
-│ ├── ci-runner/
450
-│ └── sdk/
451
-├── docs/ # Documentation
452
-├── docs-site/ # Documentation website
453
-├── scripts/ # Utility scripts
454
-├── docker-compose.yml # Docker Compose setup
455
-├── Dockerfile # Main app container
456
-└── package.json
457
-```
458
-
459
-
460
-## Testing
461
-
462
-- **Unit Tests**: Vitest (`*.test.ts`)
463
-- **E2E Tests**: Playwright
464
-- **Load Tests**: Custom load tests in `tests/load/`
465
-- **Accessibility**: `@axe-core/playwright`
466
-
467
-```bash
468
-npm run test # Run all tests
469
-npm run test:coverage # Run with coverage
470
245
```
471
246
472
-
473
-## Contributing
474
-
475
-Contributions are welcome!
476
-
477
-1. Fork the repository
478
-2. Create a feature branch
479
-3. Run lint and tests
480
-4. Open a pull request
481
-
482
247
See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards.
483
248
484
249
---
485
250
486
251
## Community
487
252
488
-- **Documentation**: [docs/index.md](docs/index.md)
253
+- **GitHub**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub)
254
+- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space)
489
255
- **Issues**: [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
490
256
- **Discussions**: [GitHub Discussions](https://github.com/swadhinbiswas/OpencodeHub/discussions)
491
257
497
259
498
260
## License
499
261
500
-MIT. See [LICENSE](LICENSE).
501
-
502
-
503
-<p align="center">
504
- <strong>Ready to get started?</strong><br>
505
- <a href="docs/getting-started/installation.md">Install OpenCodeHub</a> |
506
- <a href="docs/getting-started/quick-start.md">Quick Start</a> |
507
- <a href="docs/features/stacked-prs.md">Learn Stacked PRs</a>
508
-</p>
262
+[MIT](LICENSE) — Use it however you want.
509
263
▼Vercel Deploy.md
+127-579
1
-# Deploy OpenCodeHub to Vercel
1
+# Deploy OpenCodeHub to Vercel (Edge)
2
2
3
-> Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis
3
+> Complete guide to deploying OpenCodeHub on Vercel with Turso (LibSQL),
4
+> S3-compatible storage, and Upstash Redis.
4
5
5
-This guide walks you through deploying OpenCodeHub to Vercel using:
6
-- **Turso** - Serverless SQLite database (edge-compatible)
7
-- **Google Drive API** - File storage for repositories
8
-- **Upstash Redis** - Serverless Redis for sessions and caching
6
+OpenCodeHub is configured entirely through environment variables, so an
7
+edge/Serverless deployment is just a matter of pointing the built-in
8
+database, storage, and Redis drivers at managed services. The codebase
9
+handles all three driver families — no custom code is needed.
9
10
10
-**Estimated time:** 30-45 minutes
11
+- **Turso (LibSQL)** — edge-compatible SQLite database
12
+- **S3-compatible storage** — Cloudflare R2 (free egress, recommended),
13
+ MinIO, AWS S3, Garage, etc.
14
+- **Upstash Redis** — serverless Redis for sessions, rate limiting, queues
15
+
16
+> **Storage note:** only `local` and `s3` backends are supported.
17
+> Google Drive, OneDrive, Dropbox, GCS, Azure, and FTP backends were
18
+> removed. For Serverless, always use an S3-compatible store (`STORAGE_TYPE=s3`).
19
+
20
+**Estimated time:** 30-45 minutes
11
21
**Cost:** Free tier available for all services
12
22
13
23
---
16
26
17
27
1. [Prerequisites](#prerequisites)
18
28
2. [Setup Turso Database](#setup-turso-database)
19
-3. [Setup Google Drive Storage](#setup-google-drive-storage)
29
+3. [Setup S3-compatible Storage (R2)](#setup-s3-compatible-storage)
20
30
4. [Setup Upstash Redis](#setup-upstash-redis)
21
31
5. [Configure Project](#configure-project)
22
32
6. [Deploy to Vercel](#deploy-to-vercel)
27
37
28
38
## Prerequisites
29
39
30
-Before starting, ensure you have:
31
-
32
-- [x] GitHub account
33
-- [x] Vercel account ([vercel.com](https://vercel.com))
34
-- [x] Turso account ([turso.tech](https://turso.tech))
35
-- [x] Google Cloud account ([console.cloud.google.com](https://console.cloud.google.com))
36
-- [x] Upstash account ([upstash.com](https://upstash.com))
37
-- [x] OpenCodeHub repository forked/cloned
40
+- [ ] GitHub account
41
+- [ ] Vercel account ([vercel.com](https://vercel.com))
42
+- [ ] Turso account ([turso.tech](https://turso.tech))
43
+- [ ] Cloudflare account for R2 ([cloudflare.com](https://dash.cloudflare.com)) — or any S3-compatible store
44
+- [ ] Upstash account ([upstash.com](https://upstash.com))
45
+- [ ] OpenCodeHub repository forked/cloned
38
46
39
47
---
40
48
41
49
## 1. Setup Turso Database
42
50
43
-Turso provides edge-compatible, serverless SQLite databases perfect for Vercel.
51
+Turso provides edge-compatible, serverless SQLite databases.
44
52
45
53
### 1.1. Install Turso CLI
46
54
47
55
```bash
48
56
# macOS/Linux
49
57
curl -sSfL https://get.tur.so/install.sh | bash
50
-
51
-# Windows (PowerShell)
52
-irm get.tur.so/install.ps1 | iex
53
-
54
-# Verify installation
55
-turso --version
56
58
```
57
59
58
60
### 1.2. Login to Turso
61
63
turso auth login
62
64
```
63
65
64
-This opens your browser to authenticate.
65
-
66
66
### 1.3. Create Database
67
67
68
68
```bash
73
73
turso db tokens create opencodehub
74
74
75
75
# Get database URL
76
-turso db show opencodehub --url
76
+turso db show opencodehub
77
77
```
78
78
79
-**Save these values:**
80
-```env
81
-TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io
82
-TURSO_AUTH_TOKEN=eyJhbGciOiJ...
83
-```
79
+Save the **database URL** (starts with `libsql://`) and the **auth token** —
80
+you will need them for Vercel.
84
81
85
82
### 1.4. Initialize Database Schema
86
83
87
-```bash
88
-# Download schema
89
-curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql
90
-
91
-# Apply schema
92
-turso db shell opencodehub < schema.sql
84
+The app applies schema migrations at startup via `scripts/migrate.ts`
85
+using the committed `drizzle/` migrations:
93
86
94
-# Verify tables
95
-turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';"
87
+```bash
88
+DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run migrate
96
89
```
97
90
98
-**Expected output:**
99
-```
100
-users
101
-repositories
102
-pull_requests
103
-pr_stacks
104
-merge_queue_items
105
-ai_reviews
106
-...
107
-```
91
+On Vercel, run this once from your machine against the Turso database
92
+before the first deploy (Serverless functions have a short lifespan and
93
+should not run migrations per-request).
108
94
109
95
---
110
96
111
-## 2. Setup Google Drive Storage
112
-
113
-Google Drive API will store Git repositories and LFS files.
114
-
115
-### 2.1. Create Google Cloud Project
116
-
117
-1. Go to [Google Cloud Console](https://console.cloud.google.com)
118
-2. Click **Select a project** → **New Project**
119
-3. Name: `opencodehub-storage`
120
-4. Click **Create**
121
-
122
-### 2.2. Enable Google Drive API
123
-
124
-1. In the Cloud Console, go to **APIs & Services** → **Enable APIs**
125
-2. Search for "Google Drive API"
126
-3. Click **Enable**
97
+## 2. Setup S3-compatible Storage
127
98
128
-### 2.3. Create Service Account
99
+Use an S3-compatible object store for git objects, LFS, and artifacts.
100
+Cloudflare R2 is recommended for Serverless because egress is free.
129
101
130
-1. Go to **APIs & Services** → **Credentials**
131
-2. Click **Create Credentials** → **Service Account**
132
-3. Name: `opencodehub-storage-sa`
133
-4. Click **Create and Continue**
134
-5. Grant role: **Editor**
135
-6. Click **Done**
102
+### 2.1. Create an R2 Bucket
136
103
137
-### 2.4. Generate Service Account Key
104
+1. Cloudflare Dashboard → **R2** → **Create bucket** → name it `opencodehub`
105
+2. Go to **R2 → Manage R2 API Tokens** → **Create API Token**
106
+ - Permissions: **Object Read & Write** on the bucket
107
+3. Save the **Access Key ID** and **Secret Access Key**
138
108
139
-1. Click on the service account you just created
140
-2. Go to **Keys** tab
141
-3. Click **Add Key** → **Create new key**
142
-4. Choose **JSON**
143
-5. Click **Create** (downloads `opencodehub-storage-xxxxx.json`)
109
+Your endpoint is:
110
+`https://<ACCOUNT_ID>.r2.cloudflarestorage.com`
111
+(found in **R2 → Overview → Account ID**).
144
112
145
-### 2.5. Create Google Drive Folder
146
-
147
-1. Go to [Google Drive](https://drive.google.com)
148
-2. Create a new folder: `OpenCodeHub-Repositories`
149
-3. Right-click → **Share**
150
-4. Add the service account email: `opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com`
151
-5. Grant **Editor** access
152
-6. Copy the **Folder ID** from the URL:
153
- ```
154
- https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J
155
- ^^^^^^^^^^^^^^^^^^^^
156
- This is your folder ID
157
- ```
158
-
159
-### 2.6. Prepare Service Account Credentials
160
-
161
-Open the downloaded JSON file and extract these values:
162
-
163
-```json
164
-{
165
- "type": "service_account",
166
- "project_id": "opencodehub-storage",
167
- "private_key_id": "abc123...",
168
- "private_key": "-----BEGIN PRIVATE KEY-----\n...",
169
- "client_email": "opencodehub-storage-sa@...",
170
- "client_id": "123456789...",
171
- ...
172
-}
173
-```
174
-
175
-**Save for later:**
176
-```env
177
-GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J
178
-GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com
179
-GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
180
-GOOGLE_CLIENT_ID=123456789...
181
-```
113
+> Any S3-compatible provider works: AWS S3 (`STORAGE_ENDPOINT` empty),
114
+> MinIO (`http://minio:9000`), Garage, SeaweedFS, Ceph RGW, Wasabi, B2.
182
115
183
116
---
184
117
185
118
## 3. Setup Upstash Redis
186
119
187
-Upstash provides serverless Redis, perfect for Vercel edge functions.
120
+Upstash provides serverless Redis with a REST API — ideal for Vercel.
188
121
189
122
### 3.1. Create Redis Database
190
123
191
124
1. Go to [Upstash Console](https://console.upstash.com)
192
-2. Click **Create Database**
193
-3. **Name:** `opencodehub-sessions`
194
-4. **Region:** Choose closest to your users (e.g., `us-east-1`)
195
-5. **Type:** Regional (free tier)
196
-6. Click **Create**
125
+2. Click **Create Database** → name it `opencodehub`
126
+3. Region: **Global** (or closest to your Vercel region)
197
127
198
128
### 3.2. Get Connection Details
199
129
200
-After creation, copy these values:
201
-
202
-**REST API:**
203
-```env
204
-UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io
205
-UPSTASH_REDIS_REST_TOKEN=AaBbCc...==
206
-```
207
-
208
-**Redis URL (optional):**
209
-```env
210
-REDIS_URL=redis://default:[email protected]:port
211
-```
130
+In the database details page:
212
131
213
-### 3.3. Configure Redis for Sessions
132
+- **REST URL** — e.g. `https://your-redis.upstash.io`
133
+- **REST Token** — long alphanumeric token
214
134
215
-Upstash Redis will handle:
216
-- User sessions
217
-- Rate limiting
218
-- Temporary locks
219
-- Cache
220
-
221
-No additional setup needed!
135
+Save both.
222
136
223
137
---
224
138
225
139
## 4. Configure Project
226
140
227
-### 4.1. Update Database Configuration
228
-
229
-Create/update `src/db/turso.ts`:
141
+No code changes are required — everything is configured via environment
142
+variables that the built-in factory reads:
230
143
231
-```typescript
232
-import { drizzle } from 'drizzle-orm/libsql';
233
-import { createClient } from '@libsql/client';
144
+- Database: `src/db/index.ts` (reads `DATABASE_DRIVER` + `DATABASE_URL`)
145
+- Storage: `src/lib/storage.ts` (reads `STORAGE_TYPE` + `STORAGE_*`)
146
+- Redis: reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN`
147
+ (or `REDIS_URL`)
234
148
235
-const turso = createClient({
236
- url: process.env.TURSO_DATABASE_URL!,
237
- authToken: process.env.TURSO_AUTH_TOKEN!,
238
-});
239
-
240
-export const db = drizzle(turso);
241
-```
242
-
243
-### 4.2. Update Storage Configuration
244
-
245
-Create/update `src/lib/storage/google-drive.ts`:
246
-
247
-```typescript
248
-import { google } from 'googleapis';
249
-
250
-const auth = new google.auth.GoogleAuth({
251
- credentials: {
252
- client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
253
- private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
254
- },
255
- scopes: ['https://www.googleapis.com/auth/drive.file'],
256
-});
257
-
258
-const drive = google.drive({ version: 'v3', auth });
259
-const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID;
260
-
261
-export async function uploadFile(fileName: string, content: Buffer) {
262
- const response = await drive.files.create({
263
- requestBody: {
264
- name: fileName,
265
- parents: [folderId!],
266
- },
267
- media: {
268
- body: content,
269
- },
270
- });
271
-
272
- return response.data.id;
273
-}
274
-
275
-export async function downloadFile(fileId: string) {
276
- const response = await drive.files.get(
277
- { fileId, alt: 'media' },
278
- { responseType: 'arraybuffer' }
279
- );
280
-
281
- return Buffer.from(response.data as ArrayBuffer);
282
-}
283
-
284
-export async function deleteFile(fileId: string) {
285
- await drive.files.delete({ fileId });
286
-}
287
-```
288
-
289
-### 4.3. Update Redis Configuration
290
-
291
-Create/update `src/lib/redis.ts`:
292
-
293
-```typescript
294
-import { Redis } from '@upstash/redis';
295
-
296
-export const redis = new Redis({
297
- url: process.env.UPSTASH_REDIS_REST_URL!,
298
- token: process.env.UPSTASH_REDIS_REST_TOKEN!,
299
-});
300
-
301
-// Session management
302
-export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) {
303
- await redis.setex(sessionId, ttl, JSON.stringify(data));
304
-}
305
-
306
-export async function getSession(sessionId: string) {
307
- const data = await redis.get(sessionId);
308
- return data ? JSON.parse(data as string) : null;
309
-}
310
-
311
-export async function deleteSession(sessionId: string) {
312
- await redis.del(sessionId);
313
-}
314
-```
315
-
316
-### 4.4. Install Dependencies
317
-
318
-```bash
319
-# Add Turso client
320
-bun add @libsql/client drizzle-orm
321
-
322
-# Add Google Drive API
323
-bun add googleapis
324
-
325
-# Add Upstash Redis
326
-bun add @upstash/redis
327
-```
328
-
329
-### 4.5. Update Environment Variables Template
330
-
331
-Create `.env.example`:
149
+### 4.1. Environment Variables Template
332
150
333
151
```env
334
-# === Turso Database ===
335
-TURSO_DATABASE_URL=libsql://your-db.turso.io
336
-TURSO_AUTH_TOKEN=your-token
337
-
338
-# === Google Drive Storage ===
339
-GOOGLE_DRIVE_FOLDER_ID=your-folder-id
340
-GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com
341
-GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
342
-GOOGLE_CLIENT_ID=your-client-id
343
-
344
-# === Upstash Redis ===
152
+# === Database (Turso) ===
153
+DATABASE_DRIVER=turso
154
+DATABASE_URL=libsql://your-db.turso.io
155
+DATABASE_AUTH_TOKEN=your-token
156
+
157
+# === Storage (S3-compatible — R2 shown) ===
158
+STORAGE_TYPE=s3
159
+STORAGE_BUCKET=opencodehub
160
+STORAGE_REGION=auto
161
+STORAGE_ENDPOINT=https://<ACCOUNT_ID>.r2.cloudflarestorage.com
162
+STORAGE_ACCESS_KEY_ID=your-access-key-id
163
+STORAGE_SECRET_ACCESS_KEY=your-secret-access-key
164
+
165
+# === Redis (Upstash) ===
345
166
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
346
167
UPSTASH_REDIS_REST_TOKEN=your-token
347
168
348
169
# === Application ===
349
170
SITE_URL=https://your-app.vercel.app
171
+NODE_ENV=production
350
172
JWT_SECRET=generate-a-strong-secret
351
173
SESSION_SECRET=generate-another-strong-secret
352
174
INTERNAL_HOOK_SECRET=generate-hook-secret
175
+CRON_SECRET=generate-cron-secret
176
+RUNNER_SECRET=generate-runner-secret
177
+AI_CONFIG_ENCRYPTION_KEY=generate-encryption-key
178
+WORKFLOW_SECRET_ENCRYPTION_KEY=generate-encryption-key
179
+METRICS_TOKEN=generate-metrics-token
353
180
354
181
# === Optional: AI Review ===
355
182
AI_PROVIDER=openai
356
183
OPENAI_API_KEY=sk-...
357
184
```
358
185
359
-### 4.6. Generate Secrets
186
+### 4.2. Generate Secrets
360
187
361
188
```bash
362
-# Generate secrets
363
-openssl rand -base64 32 # JWT_SECRET
364
-openssl rand -base64 32 # SESSION_SECRET
365
-openssl rand -base64 32 # INTERNAL_HOOK_SECRET
189
+openssl rand -base64 32 # each secret
366
190
```
367
191
368
192
---
382
206
383
207
1. Go to [Vercel Dashboard](https://vercel.com/dashboard)
384
208
2. Click **Add New** → **Project**
385
-3. Import your GitHub repository
386
-4. Select `OpencodeHub`
209
+3. Import your OpenCodeHub repository
387
210
388
211
**Option B: Via CLI**
389
212
390
213
```bash
391
-cd opencodehub
392
-vercel
214
+vercel --prod
393
215
```
394
216
395
217
### 5.3. Configure Environment Variables
396
218
397
-In Vercel Dashboard:
398
-
399
-1. Go to **Project Settings** → **Environment Variables**
400
-2. Add all variables from step 4.5:
401
-
402
-**Turso:**
403
-- `TURSO_DATABASE_URL` = `libsql://opencodehub-xxxx.turso.io`
404
-- `TURSO_AUTH_TOKEN` = `eyJhbG...`
405
-
406
-**Google Drive:**
407
-- `GOOGLE_DRIVE_FOLDER_ID` = `1a2B3c4D5e6F7g8H9i0J`
408
-- `GOOGLE_SERVICE_ACCOUNT_EMAIL` = `opencodehub-storage-sa@...`
409
-- `GOOGLE_PRIVATE_KEY` = Paste entire private key (including `\n`)
410
-- `GOOGLE_CLIENT_ID` = `123456789...`
411
-
412
-**Upstash Redis:**
413
-- `UPSTASH_REDIS_REST_URL` = `https://...upstash.io`
414
-- `UPSTASH_REDIS_REST_TOKEN` = `AaBbCc...`
415
-
416
-**Application:**
417
-- `SITE_URL` = `https://your-project.vercel.app` (update later)
418
-- `JWT_SECRET` = Generated secret
419
-- `SESSION_SECRET` = Generated secret
420
-- `INTERNAL_HOOK_SECRET` = Generated secret
421
-
422
-**AI (Optional):**
423
-- `AI_PROVIDER` = `openai`
424
-- `OPENAI_API_KEY` = `sk-...`
219
+In **Vercel → Project → Settings → Environment Variables**, add every
220
+variable from section 4.1.
425
221
426
222
### 5.4. Configure Build Settings
427
223
428
-In **Project Settings** → **Build & Development Settings**:
224
+Use the defaults (Vercel auto-detects Astro):
429
225
430
-- **Framework Preset:** Astro
431
-- **Build Command:** `bun run build`
432
-- **Output Directory:** `dist`
433
-- **Install Command:** `bun install`
226
+- **Build Command:** `npm run build`
227
+- **Output Directory:** `dist/`
228
+- **Framework:** Astro
434
229
435
-### 5.5. Deploy!
230
+> The repo ships `@astrojs/vercel` and a `vercel.json`. For edge use,
231
+> switch the adapter in `astro.config.mjs` from `@astrojs/node` to
232
+> `@astrojs/vercel` with `output: "server"` (Turso + Upstash are both
233
+> edge-compatible).
436
234
437
-Click **Deploy** or run:
235
+### 5.5. Deploy!
438
236
439
237
```bash
440
238
vercel --prod
441
239
```
442
240
443
-**Deployment will:**
444
-1. Install dependencies
445
-2. Build the project
446
-3. Deploy to Vercel Edge Network
447
-4. Provide deployment URL
448
-
449
241
---
450
242
451
243
## 6. Post-Deployment
452
244
453
245
### 6.1. Update SITE_URL
454
246
455
-1. Copy your Vercel deployment URL: `https://opencodehub-abc123.vercel.app`
456
-2. Update environment variable:
457
- - Go to **Settings** → **Environment Variables**
458
- - Edit `SITE_URL` = your actual URL
459
-3. **Redeploy** (Vercel Dashboard → Deployments → Redeploy)
247
+Set `SITE_URL` to your final domain, e.g. `https://git.yourdomain.com`.
460
248
461
249
### 6.2. Create Admin User
462
250
463
-**Option A: Via Turso CLI**
464
-
465
-```bash
466
-turso db shell opencodehub
467
-
468
-INSERT INTO users (id, email, password_hash, username, role, created_at)
469
-VALUES (
470
- 'admin-user-id',
471
472
- '$2b$10$...', -- bcrypt hash of your password
473
- 'admin',
474
- 'admin',
475
- current_timestamp
476
-);
477
-```
478
-
479
-**Generate bcrypt hash:**
480
-```javascript
481
-// Run in Node.js
482
-const bcrypt = require('bcrypt');
483
-const hash = await bcrypt.hash('your-password', 10);
484
-console.log(hash);
485
-```
486
-
487
-**Option B: Via Deployment Script**
488
-
489
-Create `scripts/create-admin-vercel.ts`:
490
-
491
-```typescript
492
-import { db } from '../src/db/turso';
493
-import { users } from '../src/db/schema';
494
-import bcrypt from 'bcrypt';
495
-
496
-const email = process.env.ADMIN_EMAIL || '[email protected]';
497
-const password = process.env.ADMIN_PASSWORD || 'changeme';
498
-
499
-const hash = await bcrypt.hash(password, 10);
500
-
501
-await db.insert(users).values({
502
- id: crypto.randomUUID(),
503
- email,
504
- username: 'admin',
505
- password_hash: hash,
506
- role: 'admin',
507
- createdAt: new Date(),
508
-});
509
-
510
-console.log(`✅ Admin user created: ${email}`);
511
-```
512
-
513
-Run once:
514
251
```bash
515
-[email protected] ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts
252
+# From your machine, once, against the same Turso database:
253
+DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run scripts/seed-admin.ts
516
254
```
517
255
518
256
### 6.3. Configure Custom Domain (Optional)
519
257
520
-1. Go to **Settings** → **Domains**
521
-2. Add your custom domain: `git.yourcompany.com`
522
-3. Follow DNS configuration instructions
523
-4. Update `SITE_URL` to your custom domain
258
+In Vercel → Project → **Settings → Domains**, add your domain.
524
259
525
260
### 6.4. Setup Monitoring
526
261
527
-**Vercel Analytics:**
528
-1. Go to **Analytics** tab
529
-2. Enable **Web Analytics**
530
-3. Enable **Speed Insights**
531
-
532
-**Sentry (Optional):**
533
-```bash
534
-bun add @sentry/astro
535
-```
536
-
537
-Add to `.env`:
538
-```env
539
-SENTRY_DSN=https://[email protected]/...
540
-```
262
+- Prometheus: scrape `GET /api/metrics` with `Authorization: Bearer $METRICS_TOKEN`
263
+- Use Vercel's built-in analytics + logs for request/error tracking
541
264
542
265
---
543
266
546
268
547
269
### 7.1. Test Database Connection
548
270
549
-Visit: `https://your-app.vercel.app/api/health`
550
-
551
-Should return:
552
-```json
553
-{
554
- "status": "ok",
555
- "database": "connected",
556
- "redis": "connected",
557
- "storage": "configured"
558
-}
271
+```bash
272
+turso db shell opencodehub
273
+# Run: SELECT count(*) FROM users;
559
274
```
560
275
561
276
### 7.2. Test Storage
562
277
563
-1. Login as admin
564
-2. Create a repository
565
-3. Push a commit
566
-4. Verify file appears in Google Drive folder
278
+1. Create a repository with a large file (LFS)
279
+2. Push — verify the object appears in the R2 bucket
567
280
568
281
### 7.3. Test Redis
569
282
570
-1. Login
571
-2. Check session persists
572
-3. Logout
573
-4. Session cleared
283
+Push a git update and confirm webhooks/queues process (Redis-backed).
574
284
575
285
### 7.4. Load Test
576
286
577
287
```bash
578
288
# Install hey
579
-go install github.com/rakyll/hey@latest
289
+brew install hey # or: go install github.com/rakyll/hey@latest
580
290
581
291
# Test
582
-hey -n 1000 -c 10 https://your-app.vercel.app
292
+hey -n 1000 -c 50 https://your-app.vercel.app/api/health
583
293
```
584
294
585
-Expected: >90% success rate
586
-
587
295
---
588
296
589
297
## 8. Troubleshooting
590
298
591
299
### Database Connection Fails
592
300
593
-**Error:** `Failed to connect to Turso`
594
-
595
-**Solution:**
596
-```bash
597
-# Verify token is valid
598
-turso db tokens validate opencodehub YOUR_TOKEN
599
-
600
-# Regenerate if needed
601
-turso db tokens create opencodehub
602
-```
603
-
604
-Update environment variable and redeploy.
301
+- Verify `DATABASE_DRIVER=turso` and the `libsql://` URL + token are correct
302
+- Run `bun run migrate` locally against the same DB (see section 1.4)
605
303
606
-### Google Drive Upload Fails
304
+### Storage Upload Fails
607
305
608
-**Error:** `Insufficient permissions`
609
-
610
-**Solution:**
611
-1. Verify service account has Editor access to folder
612
-2. Check private key doesn't have extra spaces/newlines
613
-3. Test locally:
614
- ```bash
615
- bun test src/lib/storage/google-drive.test.ts
616
- ```
306
+- Verify bucket name, endpoint, and access key (S3 v4 signature)
307
+- R2: token must have **Object Read & Write** scope on the bucket
617
308
618
309
### Redis Connection Timeout
619
310
620
-**Error:** `ETIMEDOUT connecting to Redis`
621
-
622
-**Solution:**
623
-1. Verify Upstash Redis region matches Vercel deployment region
624
-2. Check REST URL and token are correct
625
-3. Test connection:
626
- ```bash
627
- curl https://YOUR_REDIS_URL/get/test \
628
- -H "Authorization: Bearer YOUR_TOKEN"
629
- ```
311
+- Confirm the Upstash REST URL/token are set (not `REDIS_URL`)
630
312
631
313
### Build Fails on Vercel
632
314
633
-**Error:** `Command "build" exited with 1`
634
-
635
-**Solution:**
636
315
```bash
637
316
# Test build locally
638
-bun run build
317
+npm run build
639
318
640
319
# Check for TypeScript errors
641
-bun run typecheck
320
+npm run typecheck
642
321
643
322
# Check environment variables are set
644
-vercel env ls
645
-```
646
-
647
-### Out of Memory
648
-
649
-**Error:** `JavaScript heap out of memory`
650
-
651
-** Solution:**
652
-Upgrade Vercel plan or optimize build:
653
-
654
-```json
655
-// package.json
656
-{
657
- "scripts": {
658
- "build": "NODE_OPTIONS=--max_old_space_size=4096 astro build"
659
- }
660
-}
323
+bun run src/lib/env-validation.ts
661
324
```
662
325
663
326
---
664
327
665
-## Cost Estimation
666
-
667
-### Free Tier Limits
668
-
669
-**Turso:**
670
-- Database: Free up to 8 GB
671
-- Rows: 500M
672
-- Locations: 1 primary + 2 replicas
673
-
674
-**Upstash Redis:**
675
-- Commands: 10,000/day
676
-- Storage: 256 MB
677
-- Bandwidth: 256 MB/month
678
-
679
-**Google Drive:**
680
-- Storage: 15 GB free
681
-- API calls: Unlimited
682
-
683
-**Vercel:**
684
-- Bandwidth: 100 GB/month
685
-- Deployments: Unlimited
686
-- Edge Functions: 100 GB-hrs
687
-
688
-### Estimated Monthly Cost
689
-
690
-**Low traffic (< 1,000 users):**
691
-- All free tier ✅
692
-
693
-**Medium traffic (1,000-10,000 users):**
694
-- Turso: $0 (within limits)
695
-- Upstash: $10/month (higher tier)
696
-- Google Drive: $2/month (Google One 100GB)
697
-- Vercel: $20/month (Pro plan)
698
-**Total: ~$32/month**
699
-
700
-**High traffic (>10,000 users):**
701
-- Turso: $29/month (Pro)
702
-- Upstash: $50/month (Pro)
703
-- Google Drive: $10/month (Google Workspace)
704
-- Vercel: $20/month (Pro)
705
-**Total: ~$109/month**
706
-
707
-
708
-## Performance Optimization
709
-
710
-### Edge Caching
711
-
712
-Add to `vercel.json`:
713
-
714
-```json
715
-{
716
- "headers": [
717
- {
718
- "source": "/assets/(.*)",
719
- "headers": [
720
- {
721
- "key": "Cache-Control",
722
- "value": "public, max-age=31536000, immutable"
723
- }
724
- ]
725
- }
726
- ]
727
-}
728
-```
729
-
730
-### Database Connection Pooling
731
-
732
-Turso handles this automatically with libSQL.
733
-
734
-### Redis Caching Strategy
735
-
736
-```typescript
737
-// Cache expensive queries
738
-async function getRepository(id: string) {
739
- const cached = await redis.get(`repo:${id}`);
740
- if (cached) return JSON.parse(cached as string);
741
-
742
- const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) });
743
- await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min
744
-
745
- return repo;
746
-}
747
-```
748
-
749
-
750
-## Security Checklist
751
-
752
-- [ ] All secrets in environment variables (not committed to Git)
753
-- [ ] HTTPS enabled (automatic on Vercel)
754
-- [ ] Database access restricted to Vercel IPs
755
-- [ ] Google Drive folder shared only with service account
756
-- [ ] Redis requires authentication
757
-- [ ] Rate limiting enabled
758
-- [ ] CORS configured properly
759
-- [ ] CSP headers set
760
-
761
-
762
-## Next Steps
763
-
764
-1. **Setup CI/CD**: Automatic deployments on push to main
765
-2. **Add Monitoring**: Set up Sentry for error tracking
766
-3. **Configure Backups**: Turso auto-backups + manual exports
767
-4. **Scale Redis**: Upgrade to Global for multi-region
768
-5. **Add CDN**: Use Vercel Edge Network or Cloudflare
769
-
770
-
771
-## Additional Resources
772
-
773
-- [Turso Documentation](https://docs.turso.tech)
774
-- [Google Drive API Reference](https://developers.google.com/drive/api/v3/reference)
775
-- [Upstash Redis Docs](https://docs.upstash.com/redis)
776
-- [Vercel Deployment Docs](https://vercel.com/docs)
777
-- [OpenCodeHub GitHub](https://github.com/swadhinbiswas/OpencodeHub)
778
-
779
-
780
-## Support
781
-
782
-**Need help?**
783
-- 💬 [Discord Community](https://discord.gg/opencodehub)
784
-- 🐛 [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
785
-- 📧 [Email Support](mailto:[email protected])
786
-
328
+## Limitations vs. Self-Hosted
787
329
788
-**Congratulations!** 🎉 Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure!
330
+- **No CI/CD runner** — pipeline execution requires a Docker host; on Vercel
331
+ use a self-hosted runner (see `packages/ci-runner` / `Dockerfile.runner`)
332
+- **No SSH Git** — SSH git requires a long-lived process
333
+ (`scripts/ssh-server.ts`); use HTTP git with PATs on Serverless
334
+- **Background worker** — scheduled jobs (`scripts/worker.ts`) need a
335
+ long-running host; run it in a small container next to your DB
789
336
▼astro.config.mjs
+16-5
1
1
import node from "@astrojs/node";
2
2
import react from "@astrojs/react";
3
-import tailwind from "@astrojs/tailwind";
4
3
import { defineConfig } from "astro/config";
4
+import icon from "astro-icon";
5
+
6
+import { config } from "dotenv";
7
+config();
8
+// Load environment variables safely
9
+const port = parseInt(process.env.PORT || "4321");
5
10
6
11
export default defineConfig({
7
12
integrations: [
8
13
react(),
9
- tailwind({
10
- applyBaseStyles: false,
11
- }),
14
+ icon(),
12
15
],
13
16
output: "server",
14
17
adapter: node({
15
18
mode: "standalone",
16
19
}),
17
20
server: {
18
- port: parseInt(process.env.PORT || "4321"),
21
+ port,
19
22
host: true,
20
23
},
21
24
vite: {
25
+ css: {
26
+ transformer: 'postcss',
27
+ },
28
+ server: {
29
+ watch: {
30
+ ignored: ['**/data/**', '**/repos/**', '**/storage/**', '**/.tmp/**', '**/postgres/**', '**/redis/**'],
31
+ },
32
+ },
22
33
optimizeDeps: {
23
34
exclude: ["nodegit", "better-sqlite3"],
24
35
},
25
36
▼bun.lock
+212-115
8
8
"@astrojs/check": "^0.9.6",
9
9
"@astrojs/node": "^8.3.0",
10
10
"@astrojs/react": "^3.6.0",
11
- "@astrojs/tailwind": "^5.1.0",
12
11
"@astrojs/vercel": "^7.7.0",
13
12
"@aws-sdk/client-s3": "^3.948.0",
14
13
"@aws-sdk/lib-storage": "^3.972.0",
16
15
"@dnd-kit/core": "^6.3.1",
17
16
"@dnd-kit/sortable": "^10.0.0",
18
17
"@dnd-kit/utilities": "^3.2.2",
18
+ "@google/genai": "^2.11.0",
19
+ "@iconify-json/file-icons": "^1.2.2",
20
+ "@iconify-json/vscode-icons": "^1.2.67",
19
21
"@libsql/client": "^0.15.15",
22
+ "@mistralai/mistralai": "^2.4.1",
20
23
"@monaco-editor/react": "^4.6.0",
21
24
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
22
25
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
51
54
"@upstash/redis": "^1.36.0",
52
55
"arctic": "^3.7.0",
53
56
"astro": "^4.11.0",
57
+ "astro-icon": "^1.1.5",
58
+ "autoprefixer": "^10.4.19",
54
59
"bcryptjs": "^2.4.3",
55
60
"better-sqlite3": "^11.1.2",
56
61
"bullmq": "^5.65.1",
60
65
"cobe": "^0.6.5",
61
66
"date-fns": "^4.1.0",
62
67
"dockerode": "^4.0.12",
68
+ "dotenv": "^17.4.2",
63
69
"drizzle-orm": "^0.45.2",
64
70
"framer-motion": "^11.18.2",
65
71
"github-markdown-css": "^5.8.1",
66
72
"graphql": "^16.12.0",
67
73
"graphql-yoga": "^5.18.0",
74
+ "highlight.js": "^11.11.1",
68
75
"ioredis": "^5.9.2",
69
76
"isomorphic-dompurify": "^2.35.0",
70
77
"isomorphic-git": "^1.25.10",
86
93
"pg": "^8.16.3",
87
94
"pino": "^10.1.0",
88
95
"pino-loki": "^3.0.0",
96
+ "postcss": "^8.5.2",
89
97
"postgres": "^3.4.7",
90
98
"prom-client": "^15.1.3",
91
99
"qrcode": "^1.5.4",
93
101
"react-dom": "^18.3.1",
94
102
"react-globe.gl": "^2.37.0",
95
103
"recharts": "^3.7.0",
104
+ "rehype-highlight": "^7.0.2",
96
105
"rehype-sanitize": "^6.0.0",
97
106
"rehype-stringify": "^10.0.1",
98
107
"remark-gfm": "^4.0.1",
103
112
"sonner": "^2.0.7",
104
113
"ssh2": "^1.15.0",
105
114
"tailwind-merge": "^2.6.0",
115
+ "tailwindcss": "^3.4.4",
106
116
"tailwindcss-animate": "^1.0.7",
107
117
"three": "^0.182.0",
108
118
"unified": "^11.0.5",
129
139
"@types/ssh2": "^1.15.0",
130
140
"@types/three": "^0.182.0",
131
141
"@vitest/coverage-v8": "^4.1.5",
132
- "autoprefixer": "^10.4.19",
133
142
"drizzle-kit": "^0.31.8",
134
143
"pino-pretty": "^13.1.3",
135
- "postcss": "^8.5.10",
136
- "tailwindcss": "^3.4.4",
137
144
"typescript": "^5.9.3",
145
+ "vite": "^5.4.11",
138
146
"vitest": "^4.1.5",
139
147
},
140
148
},
170
178
},
171
179
},
172
180
"overrides": {
173
- "brace-expansion": "^2.1.1",
181
+ "brace-expansion": "^2.0.1",
174
182
"cross-spawn": "^7.0.6",
175
183
"devalue": "^5.8.1",
176
184
"diff": "^5.2.0",
177
- "fast-uri": "^3.1.2",
185
+ "fast-uri": "^3.0.6",
178
186
"fast-xml-builder": "^1.2.0",
179
187
"fast-xml-parser": "^5.8.0",
180
188
"lodash": "^4.17.23",
185
193
"protobufjs": "^7.6.0",
186
194
"qs": "^6.15.0",
187
195
"rollup": "^4.61.0",
188
- "tar": "^7.5.16",
196
+ "tar": "^7.5.8",
189
197
"ws": "^8.18.0",
190
- "yaml": "^2.8.0",
191
198
},
192
199
"packages": {
193
200
"@acemir/cssom": ["@acemir/[email protected]", "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.31.tgz", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="],
194
201
195
202
"@alloc/quick-lru": ["@alloc/[email protected]", "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
196
203
204
+ "@antfu/install-pkg": ["@antfu/[email protected]", "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
205
+
206
+ "@antfu/utils": ["@antfu/[email protected]", "https://registry.npmmirror.com/@antfu/utils/-/utils-8.1.1.tgz", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="],
207
+
197
208
"@asamuzakjp/css-color": ["@asamuzakjp/[email protected]", "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="],
198
209
199
210
"@asamuzakjp/dom-selector": ["@asamuzakjp/[email protected]", "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.6" } }, "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ=="],
218
229
219
230
"@astrojs/react": ["@astrojs/[email protected]", "https://registry.npmmirror.com/@astrojs/react/-/react-3.6.3.tgz", { "dependencies": { "@vitejs/plugin-react": "^4.3.3", "ultrahtml": "^1.5.3", "vite": "^5.4.10" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21", "@types/react-dom": "^17.0.17 || ^18.0.6", "react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-5ihLQDH5Runddug5AZYlnp/Q5T81QxhwnWJXA9rchBAdh11c6UhBbv9Kdk7b2PkXoEU70CGWBP9hSh0VCR58eA=="],
220
231
221
- "@astrojs/tailwind": ["@astrojs/[email protected]", "https://registry.npmmirror.com/@astrojs/tailwind/-/tailwind-5.1.5.tgz", { "dependencies": { "autoprefixer": "^10.4.20", "postcss": "^8.5.1", "postcss-load-config": "^4.0.2" }, "peerDependencies": { "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", "tailwindcss": "^3.0.24" } }, "sha512-1diguZEau7FZ9vIjzE4BwavGdhD3+JkdS8zmibl1ene+EHgIU5hI0NMgRYG3yea+Niaf7cyMwjeWeLvzq/maxg=="],
222
-
223
232
"@astrojs/telemetry": ["@astrojs/[email protected]", "https://registry.npmmirror.com/@astrojs/telemetry/-/telemetry-3.1.0.tgz", { "dependencies": { "ci-info": "^4.0.0", "debug": "^4.3.4", "dlv": "^1.1.3", "dset": "^3.1.3", "is-docker": "^3.0.0", "is-wsl": "^3.0.0", "which-pm-runs": "^1.1.0" } }, "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA=="],
224
233
225
234
"@astrojs/vercel": ["@astrojs/[email protected]", "https://registry.npmmirror.com/@astrojs/vercel/-/vercel-7.8.2.tgz", { "dependencies": { "@astrojs/internal-helpers": "^0.4.1", "@vercel/analytics": "^1.3.1", "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.4", "esbuild": "^0.21.5", "fast-glob": "^3.3.2", "web-vitals": "^3.5.2" }, "peerDependencies": { "astro": "^4.2.0" } }, "sha512-U2JsfN0LzFMX5CPrcb+5bAAyqyYURP6Dk/mUIXX87r5x4baQy+juU+ntvf926YA0tR7u6jPRXHymE2axQ/l3NQ=="],
468
477
469
478
"@floating-ui/utils": ["@floating-ui/[email protected]", "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
470
479
480
+ "@google/genai": ["@google/[email protected]", "https://registry.npmmirror.com/@google/genai/-/genai-2.11.0.tgz", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-d2Csf29vS0GfHc52H0MG25ccY4FKvvbDgqDlEovLrPLF8sPegWr/GGO+LMOy85/1SnX0iV0zDAW7R8SsvWg8Vg=="],
481
+
471
482
"@graphql-tools/executor": ["@graphql-tools/[email protected]", "https://registry.npmmirror.com/@graphql-tools/executor/-/executor-1.5.3.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA=="],
472
483
473
484
"@graphql-tools/merge": ["@graphql-tools/[email protected]", "https://registry.npmmirror.com/@graphql-tools/merge/-/merge-9.1.9.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-iHUWNjRHeQRYdgIMIuChThOwoKzA9vrzYeslgfBo5eUYEyHGZCoDPjAavssoYXLwstYt1dZj2J22jSzc2DrN0Q=="],
488
499
489
500
"@grpc/proto-loader": ["@grpc/[email protected]", "https://registry.npmmirror.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="],
490
501
502
+ "@iconify-json/file-icons": ["@iconify-json/[email protected]", "https://registry.npmmirror.com/@iconify-json/file-icons/-/file-icons-1.2.2.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-ajk44wYGTiu79EAyrfNHNaxZ1/2Z1xuSOAvYUT/pAPWHc+P6n9TUZP/ccnPG18kx0WMBxmkHHQ9/zkm6ENhk+Q=="],
503
+
504
+ "@iconify-json/vscode-icons": ["@iconify-json/[email protected]", "https://registry.npmmirror.com/@iconify-json/vscode-icons/-/vscode-icons-1.2.67.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-/fObxEkBtqK2e1qf1IRjuZ4drWknAnzykdT2ngGAKESf35tCcj0Lx3MdsxYBneE5YbgvsUfOl0hbZ59R5s19vw=="],
505
+
506
+ "@iconify/tools": ["@iconify/[email protected]", "https://registry.npmmirror.com/@iconify/tools/-/tools-4.2.0.tgz", { "dependencies": { "@iconify/types": "^2.0.0", "@iconify/utils": "^2.3.0", "cheerio": "^1.1.2", "domhandler": "^5.0.3", "extract-zip": "^2.0.1", "local-pkg": "^1.1.2", "pathe": "^2.0.3", "svgo": "^3.3.2", "tar": "^7.5.2" } }, "sha512-WRxPva/ipxYkqZd1+CkEAQmd86dQmrwH0vwK89gmp2Kh2WyyVw57XbPng0NehP3x4V1LzLsXUneP1uMfTMZmUA=="],
507
+
508
+ "@iconify/types": ["@iconify/[email protected]", "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
509
+
510
+ "@iconify/utils": ["@iconify/[email protected]", "https://registry.npmmirror.com/@iconify/utils/-/utils-2.3.0.tgz", { "dependencies": { "@antfu/install-pkg": "^1.0.0", "@antfu/utils": "^8.1.0", "@iconify/types": "^2.0.0", "debug": "^4.4.0", "globals": "^15.14.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "mlly": "^1.7.4" } }, "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA=="],
511
+
491
512
"@img/sharp-darwin-arm64": ["@img/[email protected]", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
492
513
493
514
"@img/sharp-darwin-x64": ["@img/[email protected]", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
584
605
585
606
"@mapbox/node-pre-gyp": ["@mapbox/[email protected]", "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg=="],
586
607
608
+ "@mistralai/mistralai": ["@mistralai/[email protected]", "https://registry.npmmirror.com/@mistralai/mistralai/-/mistralai-2.4.1.tgz", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-vNpR2GR76aW7fK8Dcu1NRx3fdSPO1QruRDo4GbfXfkro2mvp194xIAvJ2BP4ZykzyWtJoDmBe+Qsk9D443aqlg=="],
609
+
587
610
"@monaco-editor/loader": ["@monaco-editor/[email protected]", "https://registry.npmmirror.com/@monaco-editor/loader/-/loader-1.7.0.tgz", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="],
588
611
589
612
"@monaco-editor/react": ["@monaco-editor/[email protected]", "https://registry.npmmirror.com/@monaco-editor/react/-/react-4.7.0.tgz", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="],
1166
1189
1167
1190
"@types/react-dom": ["@types/[email protected]", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
1168
1191
1192
+ "@types/retry": ["@types/[email protected]", "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
1193
+
1169
1194
"@types/ssh2": ["@types/[email protected]", "https://registry.npmmirror.com/@types/ssh2/-/ssh2-1.15.5.tgz", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
1170
1195
1171
1196
"@types/stats.js": ["@types/[email protected]", "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
1188
1213
1189
1214
"@types/ws": ["@types/[email protected]", "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
1190
1215
1216
+ "@types/yauzl": ["@types/[email protected]", "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
1217
+
1191
1218
"@ungap/structured-clone": ["@ungap/[email protected]", "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
1192
1219
1193
1220
"@upstash/redis": ["@upstash/[email protected]", "https://registry.npmmirror.com/@upstash/redis/-/redis-1.38.0.tgz", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg=="],
1296
1323
1297
1324
"astro": ["[email protected]", "https://registry.npmmirror.com/astro/-/astro-4.16.19.tgz", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.1.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.7.2", "cssesc": "^3.0.0", "debug": "^4.3.7", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", "fast-glob": "^3.3.2", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.14", "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", "ora": "^8.1.1", "p-limit": "^6.1.0", "p-queue": "^8.0.1", "preferred-pm": "^4.0.0", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.23.1", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.11", "vitefu": "^1.0.4", "which-pm": "^3.0.0", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-baeSswPC5ZYvhGDoj25L2FuzKRWMgx105FetOPQVJFMCAp0o08OonYC7AhwsFdhvp7GapqjnC1Fe3lKb2lupYw=="],
1298
1325
1326
+ "astro-icon": ["[email protected]", "https://registry.npmmirror.com/astro-icon/-/astro-icon-1.1.5.tgz", { "dependencies": { "@iconify/tools": "^4.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^2.1.30" } }, "sha512-CJYS5nWOw9jz4RpGWmzNQY7D0y2ZZacH7atL2K9DeJXJVaz7/5WrxeyIxO8KASk1jCM96Q4LjRx/F3R+InjJrw=="],
1327
+
1299
1328
"async-lock": ["[email protected]", "https://registry.npmmirror.com/async-lock/-/async-lock-1.4.1.tgz", {}, "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="],
1300
1329
1301
1330
"async-sema": ["[email protected]", "https://registry.npmmirror.com/async-sema/-/async-sema-3.1.1.tgz", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="],
1356
1385
1357
1386
"bl": ["[email protected]", "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
1358
1387
1388
+ "boolbase": ["[email protected]", "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
1389
+
1359
1390
"bowser": ["[email protected]", "https://registry.npmmirror.com/bowser/-/bowser-2.14.1.tgz", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
1360
1391
1361
1392
"boxen": ["[email protected]", "https://registry.npmmirror.com/boxen/-/boxen-7.1.1.tgz", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0" } }, "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog=="],
1368
1399
1369
1400
"buffer": ["[email protected]", "https://registry.npmmirror.com/buffer/-/buffer-5.6.0.tgz", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="],
1370
1401
1402
+ "buffer-crc32": ["[email protected]", "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
1403
+
1404
+ "buffer-equal-constant-time": ["[email protected]", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
1405
+
1371
1406
"buffer-from": ["[email protected]", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
1372
1407
1373
1408
"buildcheck": ["[email protected]", "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.7.tgz", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="],
1408
1443
1409
1444
"chardet": ["[email protected]", "https://registry.npmmirror.com/chardet/-/chardet-2.1.1.tgz", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
1410
1445
1411
- "chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
1446
+ "cheerio": ["[email protected]", "https://registry.npmmirror.com/cheerio/-/cheerio-1.2.0.tgz", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="],
1447
+
1448
+ "cheerio-select": ["[email protected]", "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
1449
+
1450
+ "chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
1412
1451
1413
1452
"chownr": ["[email protected]", "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
1414
1453
1462
1501
1463
1502
"conf": ["[email protected]", "https://registry.npmmirror.com/conf/-/conf-12.0.0.tgz", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "atomically": "^2.0.2", "debounce-fn": "^5.1.2", "dot-prop": "^8.0.2", "env-paths": "^3.0.0", "json-schema-typed": "^8.0.1", "semver": "^7.5.4", "uint8array-extras": "^0.3.0" } }, "sha512-fIWyWUXrJ45cHCIQX+Ck1hrZDIf/9DR0P0Zewn3uNht28hbt5OfGUq8rRWsxi96pZWPyBEd0eY9ama01JTaknA=="],
1464
1503
1504
+ "confbox": ["[email protected]", "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
1505
+
1465
1506
"consola": ["[email protected]", "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
1466
1507
1467
1508
"convert-source-map": ["[email protected]", "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
1478
1519
1479
1520
"cross-spawn": ["[email protected]", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
1480
1521
1522
+ "css-select": ["[email protected]", "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
1523
+
1481
1524
"css-tree": ["[email protected]", "https://registry.npmmirror.com/css-tree/-/css-tree-3.2.1.tgz", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
1482
1525
1526
+ "css-what": ["[email protected]", "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
1527
+
1483
1528
"cssesc": ["[email protected]", "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
1484
1529
1530
+ "csso": ["[email protected]", "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
1531
+
1485
1532
"cssstyle": ["[email protected]", "https://registry.npmmirror.com/cssstyle/-/cssstyle-6.2.0.tgz", { "dependencies": { "@asamuzakjp/css-color": "^5.0.1", "@csstools/css-syntax-patches-for-csstree": "^1.0.28", "css-tree": "^3.1.0", "lru-cache": "^11.2.6" } }, "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig=="],
1486
1533
1487
1534
"csstype": ["[email protected]", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
1562
1609
1563
1610
"destroy": ["[email protected]", "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
1564
1611
1565
- "detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
1612
+ "detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
1566
1613
1567
1614
"detect-node-es": ["[email protected]", "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
1568
1615
1586
1633
1587
1634
"dockerode": ["[email protected]", "https://registry.npmmirror.com/dockerode/-/dockerode-4.0.12.tgz", { "dependencies": { "@balena/dockerignore": "^1.0.2", "@grpc/grpc-js": "^1.11.1", "@grpc/proto-loader": "^0.7.13", "docker-modem": "^5.0.7", "protobufjs": "^7.3.2", "tar-fs": "^2.1.4", "uuid": "^10.0.0" } }, "sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw=="],
1588
1635
1636
+ "dom-serializer": ["[email protected]", "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
1637
+
1638
+ "domelementtype": ["[email protected]", "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
1639
+
1640
+ "domhandler": ["[email protected]", "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
1641
+
1589
1642
"dompurify": ["[email protected]", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.8.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ=="],
1590
1643
1644
+ "domutils": ["[email protected]", "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
1645
+
1591
1646
"dot-prop": ["[email protected]", "https://registry.npmmirror.com/dot-prop/-/dot-prop-8.0.2.tgz", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
1592
1647
1648
+ "dotenv": ["[email protected]", "https://registry.npmmirror.com/dotenv/-/dotenv-17.4.2.tgz", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
1649
+
1593
1650
"drizzle-kit": ["[email protected]", "https://registry.npmmirror.com/drizzle-kit/-/drizzle-kit-0.31.10.tgz", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw=="],
1594
1651
1595
1652
"drizzle-orm": ["[email protected]", "https://registry.npmmirror.com/drizzle-orm/-/drizzle-orm-0.45.2.tgz", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q=="],
1602
1659
1603
1660
"eastasianwidth": ["[email protected]", "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
1604
1661
1662
+ "ecdsa-sig-formatter": ["[email protected]", "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
1663
+
1605
1664
"ee-first": ["[email protected]", "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
1606
1665
1607
1666
"electron-to-chromium": ["[email protected]", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="],
1616
1675
1617
1676
"encoding": ["[email protected]", "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="],
1618
1677
1678
+ "encoding-sniffer": ["[email protected]", "https://registry.npmmirror.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
1679
+
1619
1680
"end-of-stream": ["[email protected]", "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
1620
1681
1621
1682
"entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-8.0.0.tgz", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="],
1662
1723
1663
1724
"exponential-backoff": ["[email protected]", "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
1664
1725
1726
+ "exsolve": ["[email protected]", "https://registry.npmmirror.com/exsolve/-/exsolve-1.1.0.tgz", {}, "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw=="],
1727
+
1665
1728
"extend": ["[email protected]", "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
1666
1729
1667
1730
"extend-shallow": ["[email protected]", "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
1668
1731
1732
+ "extract-zip": ["[email protected]", "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
1733
+
1669
1734
"fast-copy": ["[email protected]", "https://registry.npmmirror.com/fast-copy/-/fast-copy-4.0.3.tgz", {}, "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw=="],
1670
1735
1671
1736
"fast-deep-equal": ["[email protected]", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
1684
1749
1685
1750
"fastq": ["[email protected]", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
1686
1751
1752
+ "fd-slicer": ["[email protected]", "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
1753
+
1687
1754
"fdir": ["[email protected]", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
1688
1755
1689
1756
"fetch-blob": ["[email protected]", "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
1754
1821
1755
1822
"get-proto": ["[email protected]", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
1756
1823
1757
- "get-stream": ["[email protected]", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
1824
+ "get-stream": ["[email protected]", "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
1758
1825
1759
1826
"get-tsconfig": ["[email protected]", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="],
1760
1827
1768
1835
1769
1836
"glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
1770
1837
1838
+ "globals": ["[email protected]", "https://registry.npmmirror.com/globals/-/globals-15.15.0.tgz", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
1839
+
1771
1840
"globe.gl": ["[email protected]", "https://registry.npmmirror.com/globe.gl/-/globe.gl-2.46.1.tgz", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.179 <1", "three-globe": "^2.45", "three-render-objects": "^1.41" } }, "sha512-h+OvX52EBIPLtM0/2JkM+JZ9gPAhPJ4y3+hxUwD5Ey/O0Zk2ockuTiJ71bZbnNBGmNiIZzA5Vr3TMT0b3d35IQ=="],
1772
1841
1842
+ "google-auth-library": ["[email protected]", "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.9.0.tgz", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg=="],
1843
+
1773
1844
"google-logging-utils": ["[email protected]", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
1774
1845
1775
1846
"gopd": ["[email protected]", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
1822
1893
1823
1894
"help-me": ["[email protected]", "https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="],
1824
1895
1896
+ "highlight.js": ["[email protected]", "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
1897
+
1825
1898
"html-encoding-sniffer": ["[email protected]", "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="],
1826
1899
1827
1900
"html-escaper": ["[email protected]", "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
1828
1901
1829
1902
"html-void-elements": ["[email protected]", "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
1830
1903
1904
+ "htmlparser2": ["[email protected]", "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.1.0.tgz", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
1905
+
1831
1906
"http-cache-semantics": ["[email protected]", "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
1832
1907
1833
1908
"http-errors": ["[email protected]", "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
1952
2027
1953
2028
"jsonfile": ["[email protected]", "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
1954
2029
2030
+ "jwa": ["[email protected]", "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
2031
+
2032
+ "jws": ["[email protected]", "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
2033
+
1955
2034
"kapsule": ["[email protected]", "https://registry.npmmirror.com/kapsule/-/kapsule-1.16.3.tgz", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="],
1956
2035
1957
2036
"keyv": ["[email protected]", "https://registry.npmmirror.com/keyv/-/keyv-5.6.0.tgz", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
1960
2039
1961
2040
"kleur": ["[email protected]", "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
1962
2041
2042
+ "kolorist": ["[email protected]", "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
2043
+
1963
2044
"libsql": ["[email protected]", "https://registry.npmmirror.com/libsql/-/libsql-0.5.29.tgz", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.29", "@libsql/darwin-x64": "0.5.29", "@libsql/linux-arm-gnueabihf": "0.5.29", "@libsql/linux-arm-musleabihf": "0.5.29", "@libsql/linux-arm64-gnu": "0.5.29", "@libsql/linux-arm64-musl": "0.5.29", "@libsql/linux-x64-gnu": "0.5.29", "@libsql/linux-x64-musl": "0.5.29", "@libsql/win32-x64-msvc": "0.5.29" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg=="],
1964
2045
1965
2046
"lightningcss": ["[email protected]", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
1992
2073
1993
2074
"load-yaml-file": ["[email protected]", "https://registry.npmmirror.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.13.0", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw=="],
1994
2075
2076
+ "local-pkg": ["[email protected]", "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.2.1.tgz", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q=="],
2077
+
1995
2078
"locate-path": ["[email protected]", "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
1996
2079
1997
2080
"lodash": ["[email protected]", "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
2014
2097
2015
2098
"lowercase-keys": ["[email protected]", "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="],
2016
2099
2100
+ "lowlight": ["[email protected]", "https://registry.npmmirror.com/lowlight/-/lowlight-3.3.0.tgz", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="],
2101
+
2017
2102
"lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
2018
2103
2019
2104
"lru.min": ["[email protected]", "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.4.tgz", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="],
2156
2241
2157
2242
"mkdirp-classic": ["[email protected]", "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
2158
2243
2244
+ "mlly": ["[email protected]", "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
2245
+
2159
2246
"module-details-from-path": ["[email protected]", "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
2160
2247
2161
2248
"monaco-editor": ["[email protected]", "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.50.0.tgz", {}, "sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA=="],
2220
2307
2221
2308
"normalize-url": ["[email protected]", "https://registry.npmmirror.com/normalize-url/-/normalize-url-8.1.1.tgz", {}, "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ=="],
2222
2309
2310
+ "nth-check": ["[email protected]", "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
2311
+
2223
2312
"oauth4webapi": ["[email protected]", "https://registry.npmmirror.com/oauth4webapi/-/oauth4webapi-3.8.6.tgz", {}, "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ=="],
2224
2313
2225
2314
"object-assign": ["[email protected]", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
2260
2349
2261
2350
"p-queue": ["[email protected]", "https://registry.npmmirror.com/p-queue/-/p-queue-8.1.1.tgz", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
2262
2351
2352
+ "p-retry": ["[email protected]", "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
2353
+
2263
2354
"p-timeout": ["[email protected]", "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.4.tgz", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
2264
2355
2265
2356
"p-try": ["[email protected]", "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
2266
2357
2267
2358
"package-json-from-dist": ["[email protected]", "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
2268
2359
2360
+ "package-manager-detector": ["[email protected]", "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.7.0.tgz", {}, "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ=="],
2361
+
2269
2362
"pako": ["[email protected]", "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
2270
2363
2271
2364
"parse-latin": ["[email protected]", "https://registry.npmmirror.com/parse-latin/-/parse-latin-7.0.0.tgz", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
2272
2365
2273
2366
"parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-8.0.1.tgz", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="],
2274
2367
2368
+ "parse5-htmlparser2-tree-adapter": ["[email protected]", "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
2369
+
2370
+ "parse5-parser-stream": ["[email protected]", "https://registry.npmmirror.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="],
2371
+
2275
2372
"path-browserify": ["[email protected]", "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
2276
2373
2277
2374
"path-exists": ["[email protected]", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
2288
2385
2289
2386
"pathe": ["[email protected]", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
2290
2387
2388
+ "pend": ["[email protected]", "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
2389
+
2291
2390
"pg": ["[email protected]", "https://registry.npmmirror.com/pg/-/pg-8.21.0.tgz", { "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.14.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA=="],
2292
2391
2293
2392
"pg-cloudflare": ["[email protected]", "https://registry.npmmirror.com/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="],
2326
2425
2327
2426
"pkg-dir": ["[email protected]", "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
2328
2427
2428
+ "pkg-types": ["[email protected]", "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.1.tgz", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="],
2429
+
2329
2430
"playwright": ["[email protected]", "https://registry.npmmirror.com/playwright/-/playwright-1.60.0.tgz", { "dependencies": { "playwright-core": "1.60.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA=="],
2330
2431
2331
2432
"playwright-core": ["[email protected]", "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.60.0.tgz", { "bin": { "playwright-core": "cli.js" } }, "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA=="],
2338
2439
2339
2440
"possible-typed-array-names": ["[email protected]", "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
2340
2441
2341
- "postcss": ["[email protected]", "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="],
2442
+ "postcss": ["[email protected]", "https://registry.npmmirror.com/postcss/-/postcss-8.5.25.tgz", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw=="],
2342
2443
2343
2444
"postcss-import": ["[email protected]", "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
2344
2445
2398
2499
2399
2500
"qrcode": ["[email protected]", "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
2400
2501
2502
+ "quansync": ["[email protected]", "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
2503
+
2401
2504
"queue-microtask": ["[email protected]", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
2402
2505
2403
2506
"quick-format-unescaped": ["[email protected]", "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
2432
2535
2433
2536
"readable-stream": ["[email protected]", "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
2434
2537
2435
- "readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
2538
+ "readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
2436
2539
2437
2540
"real-require": ["[email protected]", "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
2438
2541
2454
2557
2455
2558
"rehype": ["[email protected]", "https://registry.npmmirror.com/rehype/-/rehype-13.0.2.tgz", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="],
2456
2559
2560
+ "rehype-highlight": ["[email protected]", "https://registry.npmmirror.com/rehype-highlight/-/rehype-highlight-7.0.2.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-text": "^4.0.0", "lowlight": "^3.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA=="],
2561
+
2457
2562
"rehype-parse": ["[email protected]", "https://registry.npmmirror.com/rehype-parse/-/rehype-parse-9.0.1.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="],
2458
2563
2459
2564
"rehype-raw": ["[email protected]", "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
2504
2609
2505
2610
"retext-stringify": ["[email protected]", "https://registry.npmmirror.com/retext-stringify/-/retext-stringify-4.0.0.tgz", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="],
2506
2611
2507
- "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
2612
+ "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
2508
2613
2509
2614
"reusify": ["[email protected]", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
2510
2615
2526
2631
2527
2632
"safer-buffer": ["[email protected]", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
2528
2633
2634
+ "sax": ["[email protected]", "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
2635
+
2529
2636
"saxes": ["[email protected]", "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
2530
2637
2531
2638
"scheduler": ["[email protected]", "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
2648
2755
2649
2756
"supports-preserve-symlinks-flag": ["[email protected]", "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
2650
2757
2758
+ "svgo": ["[email protected]", "https://registry.npmmirror.com/svgo/-/svgo-3.3.4.tgz", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg=="],
2759
+
2651
2760
"symbol-tree": ["[email protected]", "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
2652
2761
2653
2762
"tailwind-merge": ["[email protected]", "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.1.tgz", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="],
2742
2851
2743
2852
"typescript-auto-import-cache": ["[email protected]", "https://registry.npmmirror.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="],
2744
2853
2854
+ "ufo": ["[email protected]", "https://registry.npmmirror.com/ufo/-/ufo-1.6.4.tgz", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
2855
+
2745
2856
"uint8array-extras": ["[email protected]", "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-0.3.0.tgz", {}, "sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA=="],
2746
2857
2747
2858
"ultrahtml": ["[email protected]", "https://registry.npmmirror.com/ultrahtml/-/ultrahtml-1.6.0.tgz", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
2800
2911
2801
2912
"victory-vendor": ["[email protected]", "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-37.3.6.tgz", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
2802
2913
2803
- "vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-8.0.16.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="],
2914
+ "vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
2804
2915
2805
2916
"vitefu": ["[email protected]", "https://registry.npmmirror.com/vitefu/-/vitefu-1.1.3.tgz", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="],
2806
2917
2852
2963
2853
2964
"webidl-conversions": ["[email protected]", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="],
2854
2965
2966
+ "whatwg-encoding": ["[email protected]", "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
2967
+
2855
2968
"whatwg-mimetype": ["[email protected]", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="],
2856
2969
2857
2970
"whatwg-url": ["[email protected]", "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-16.0.1.tgz", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="],
2902
3015
2903
3016
"yargs-parser": ["[email protected]", "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
2904
3017
3018
+ "yauzl": ["[email protected]", "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
3019
+
2905
3020
"yocto-queue": ["[email protected]", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.2.tgz", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
2906
3021
2907
3022
"yoctocolors-cjs": ["[email protected]", "https://registry.npmmirror.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="],
2918
3033
2919
3034
"@asamuzakjp/dom-selector/lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="],
2920
3035
2921
- "@astrojs/check/chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
2922
-
2923
- "@astrojs/react/vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
2924
-
2925
3036
"@astrojs/vercel/esbuild": ["[email protected]", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
2926
3037
2927
3038
"@babel/code-frame/js-tokens": ["[email protected]", "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
2944
3055
2945
3056
"@isaacs/cliui/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
2946
3057
2947
- "@mapbox/node-pre-gyp/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2948
-
2949
3058
"@mapbox/node-pre-gyp/node-fetch": ["[email protected]", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
2950
3059
2951
3060
"@opentelemetry/instrumentation-pg/@types/pg": ["@types/[email protected]", "https://registry.npmmirror.com/@types/pg/-/pg-8.15.6.tgz", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="],
2998
3107
2999
3108
"@vercel/nft/estree-walker": ["[email protected]", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
3000
3109
3001
- "@vitejs/plugin-react/vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
3002
-
3003
3110
"ansi-align/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3004
3111
3005
3112
"ansi-escapes/type-fest": ["[email protected]", "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
3016
3123
3017
3124
"astro/tinyexec": ["[email protected]", "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
3018
3125
3019
- "astro/vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
3020
-
3021
3126
"bl/readable-stream": ["[email protected]", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
3022
3127
3023
3128
"bullmq/ioredis": ["[email protected]", "https://registry.npmmirror.com/ioredis/-/ioredis-5.10.1.tgz", { "dependencies": { "@ioredis/commands": "1.5.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA=="],
3026
3131
3027
3132
"cacache/glob": ["[email protected]", "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
3028
3133
3134
+ "cacheable-request/get-stream": ["[email protected]", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
3135
+
3029
3136
"cacheable-request/mimic-response": ["[email protected]", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
3030
3137
3031
- "chokidar/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
3138
+ "cheerio/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3139
+
3140
+ "cheerio/whatwg-mimetype": ["[email protected]", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
3032
3141
3033
3142
"cli-progress/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3034
3143
3044
3153
3045
3154
"cross-spawn/which": ["[email protected]", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
3046
3155
3156
+ "csso/css-tree": ["[email protected]", "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
3157
+
3047
3158
"cssstyle/lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="],
3048
3159
3049
3160
"docker-modem/readable-stream": ["[email protected]", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
3050
3161
3162
+ "dom-serializer/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
3163
+
3051
3164
"dot-prop/type-fest": ["[email protected]", "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
3052
3165
3053
3166
"encoding/iconv-lite": ["[email protected]", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
3054
3167
3168
+ "encoding-sniffer/iconv-lite": ["[email protected]", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
3169
+
3055
3170
"fast-glob/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
3056
3171
3057
3172
"figlet/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
3064
3179
3065
3180
"gradient-string/chalk": ["[email protected]", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
3066
3181
3067
- "gray-matter/js-yaml": ["[email protected]", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
3182
+ "gray-matter/js-yaml": ["[email protected]", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.15.0.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="],
3068
3183
3069
3184
"hast-util-from-html/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3070
3185
3071
3186
"hast-util-raw/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3072
3187
3188
+ "htmlparser2/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
3189
+
3073
3190
"inquirer/ora": ["[email protected]", "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
3074
3191
3075
3192
"inquirer/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3078
3195
3079
3196
"istanbul-reports/html-escaper": ["[email protected]", "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="],
3080
3197
3081
- "lightningcss/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
3198
+ "libsql/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
3082
3199
3083
- "load-yaml-file/js-yaml": ["[email protected]", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
3200
+ "load-yaml-file/js-yaml": ["[email protected]", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.15.0.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="],
3084
3201
3085
3202
"log-symbols/is-unicode-supported": ["[email protected]", "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
3086
3203
3092
3209
3093
3210
"minipass-sized/minipass": ["[email protected]", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
3094
3211
3095
- "node-gyp/env-paths": ["[email protected]", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
3212
+ "mlly/pkg-types": ["[email protected]", "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
3096
3213
3097
- "node-gyp-build-optional-packages/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
3214
+ "node-gyp/env-paths": ["[email protected]", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
3098
3215
3099
3216
"nodegit/tar-fs": ["[email protected]", "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.1.2.tgz", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw=="],
3100
3217
3106
3223
3107
3224
"p-locate/p-limit": ["[email protected]", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
3108
3225
3226
+ "parse5-htmlparser2-tree-adapter/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3227
+
3228
+ "parse5-parser-stream/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3229
+
3109
3230
"playwright/fsevents": ["[email protected]", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
3110
3231
3111
- "postcss/nanoid": ["[email protected]", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
3232
+ "postcss/nanoid": ["[email protected]", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.16.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="],
3112
3233
3113
3234
"postcss-nested/postcss-selector-parser": ["[email protected]", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
3114
3235
3115
- "prebuild-install/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
3236
+ "promise-retry/retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
3116
3237
3117
3238
"prompts/kleur": ["[email protected]", "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
3118
3239
3128
3249
3129
3250
"send/debug": ["[email protected]", "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
3130
3251
3131
- "sharp/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
3132
-
3133
3252
"ssh2/nan": ["[email protected]", "https://registry.npmmirror.com/nan/-/nan-2.27.0.tgz", {}, "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ=="],
3134
3253
3135
3254
"stream-browserify/readable-stream": ["[email protected]", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
3140
3259
3141
3260
"sucrase/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
3142
3261
3262
+ "svgo/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
3263
+
3264
+ "svgo/css-tree": ["[email protected]", "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
3265
+
3266
+ "tailwindcss/chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
3267
+
3143
3268
"tailwindcss/postcss-selector-parser": ["[email protected]", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
3144
3269
3145
3270
"tar/chownr": ["[email protected]", "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
3150
3275
3151
3276
"tsx/esbuild": ["[email protected]", "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.0.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
3152
3277
3278
+ "vite/esbuild": ["[email protected]", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
3279
+
3280
+ "vitest/vite": ["[email protected]", "https://registry.npmmirror.com/vite/-/vite-8.0.16.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="],
3281
+
3153
3282
"vscode-css-languageservice/vscode-languageserver-types": ["[email protected]", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
3154
3283
3155
3284
"vscode-json-languageservice/jsonc-parser": ["[email protected]", "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
3156
3285
3157
3286
"vscode-languageserver-protocol/vscode-languageserver-types": ["[email protected]", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
3158
3287
3288
+ "whatwg-encoding/iconv-lite": ["[email protected]", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
3289
+
3159
3290
"wrap-ansi/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
3160
3291
3161
3292
"wrap-ansi-cjs/ansi-styles": ["[email protected]", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
3164
3295
3165
3296
"yaml-language-server/request-light": ["[email protected]", "https://registry.npmmirror.com/request-light/-/request-light-0.5.8.tgz", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
3166
3297
3167
- "yargs/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3168
-
3169
- "@astrojs/check/chokidar/readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
3298
+ "yaml-language-server/yaml": ["[email protected]", "https://registry.npmmirror.com/yaml/-/yaml-2.7.1.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
3170
3299
3171
- "@astrojs/react/vite/esbuild": ["[email protected]", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
3300
+ "yargs/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3172
3301
3173
3302
"@astrojs/vercel/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
3174
3303
3268
3397
3269
3398
"@types/ssh2/@types/node/undici-types": ["[email protected]", "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
3270
3399
3271
- "@vitejs/plugin-react/vite/esbuild": ["[email protected]", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
3272
-
3273
3400
"ansi-align/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3274
3401
3275
3402
"astro/boxen/camelcase": ["[email protected]", "https://registry.npmmirror.com/camelcase/-/camelcase-8.0.0.tgz", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
3330
3457
3331
3458
"bullmq/ioredis/@ioredis/commands": ["@ioredis/[email protected]", "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.5.1.tgz", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
3332
3459
3460
+ "cheerio/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
3461
+
3333
3462
"cli-progress/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3334
3463
3335
3464
"cli-table3/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3340
3469
3341
3470
"cross-spawn/which/isexe": ["[email protected]", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
3342
3471
3472
+ "csso/css-tree/mdn-data": ["[email protected]", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
3473
+
3343
3474
"got/decompress-response/mimic-response": ["[email protected]", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
3344
3475
3345
3476
"gradient-string/chalk/ansi-styles": ["[email protected]", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
3372
3503
3373
3504
"minipass-sized/minipass/yallist": ["[email protected]", "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
3374
3505
3506
+ "mlly/pkg-types/confbox": ["[email protected]", "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
3507
+
3375
3508
"nodegit/tar-fs/tar-stream": ["[email protected]", "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.2.0.tgz", { "dependencies": { "b4a": "^1.6.4", "bare-fs": "^4.5.5", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg=="],
3376
3509
3377
3510
"ora/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
3378
3511
3379
3512
"ora/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
3380
3513
3514
+ "parse5-htmlparser2-tree-adapter/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
3515
+
3516
+ "parse5-parser-stream/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
3517
+
3381
3518
"qrcode/yargs/cliui": ["[email protected]", "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
3382
3519
3383
3520
"qrcode/yargs/string-width": ["[email protected]", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
3390
3527
3391
3528
"string-width/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
3392
3529
3530
+ "svgo/css-tree/mdn-data": ["[email protected]", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
3531
+
3532
+ "tailwindcss/chokidar/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
3533
+
3534
+ "tailwindcss/chokidar/readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
3535
+
3393
3536
"tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="],
3394
3537
3395
3538
"tsx/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="],
3442
3585
3443
3586
"tsx/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="],
3444
3587
3445
- "wrap-ansi-cjs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3588
+ "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
3446
3589
3447
- "wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
3590
+ "vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
3448
3591
3449
- "yargs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3592
+ "vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
3450
3593
3451
- "@astrojs/react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
3594
+ "vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
3452
3595
3453
- "@astrojs/react/vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
3596
+ "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
3454
3597
3455
- "@astrojs/react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
3598
+ "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
3456
3599
3457
- "@astrojs/react/vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
3600
+ "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
3458
3601
3459
- "@astrojs/react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
3602
+ "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
3460
3603
3461
- "@astrojs/react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
3604
+ "vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
3462
3605
3463
- "@astrojs/react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
3606
+ "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
3464
3607
3465
- "@astrojs/react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
3608
+ "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
3466
3609
3467
- "@astrojs/react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
3610
+ "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
3468
3611
3469
- "@astrojs/react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
3612
+ "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
3470
3613
3471
- "@astrojs/react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
3614
+ "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
3472
3615
3473
- "@astrojs/react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
3616
+ "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
3474
3617
3475
- "@astrojs/react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
3618
+ "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
3476
3619
3477
- "@astrojs/react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
3620
+ "vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
3478
3621
3479
- "@astrojs/react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
3622
+ "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
3480
3623
3481
- "@astrojs/react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
3624
+ "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
3482
3625
3483
- "@astrojs/react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
3626
+ "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
3484
3627
3485
- "@astrojs/react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
3628
+ "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
3486
3629
3487
- "@astrojs/react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
3630
+ "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
3488
3631
3489
- "@astrojs/react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
3632
+ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
3490
3633
3491
- "@astrojs/react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
3634
+ "wrap-ansi-cjs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3492
3635
3493
- "@astrojs/react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
3636
+ "wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
3494
3637
3495
- "@astrojs/react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
3638
+ "yargs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
3496
3639
3497
3640
"@mapbox/node-pre-gyp/node-fetch/whatwg-url/tr46": ["[email protected]", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
3498
3641
3499
3642
"@mapbox/node-pre-gyp/node-fetch/whatwg-url/webidl-conversions": ["[email protected]", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
3500
3643
3501
- "@vitejs/plugin-react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
3502
-
3503
- "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
3504
-
3505
- "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
3506
-
3507
- "@vitejs/plugin-react/vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
3508
-
3509
- "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
3510
-
3511
- "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
3512
-
3513
- "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
3514
-
3515
- "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
3516
-
3517
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
3518
-
3519
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
3520
-
3521
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
3522
-
3523
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
3524
-
3525
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
3526
-
3527
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
3528
-
3529
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
3530
-
3531
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
3532
-
3533
- "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
3534
-
3535
- "@vitejs/plugin-react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
3536
-
3537
- "@vitejs/plugin-react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
3538
-
3539
- "@vitejs/plugin-react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
3540
-
3541
- "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
3542
-
3543
- "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
3544
-
3545
- "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
3546
-
3547
3644
"astro/boxen/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
3548
3645
3549
3646
"astro/boxen/string-width/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
3550
3647
▼cli/src/commands/stack/index.ts
+0-4
270
270
.action(async () => {
271
271
try {
272
272
const roots = await getStackTopology(git);
273
- console.log(chalk.hex("#6272a4")("No stack found."));
274
- return;
275
- }
276
-
277
273
const { printSectionHeader, formatBadge } = await import("../../lib/formatter.js");
278
274
279
275
// Use Dracula colors directly or via chalk
280
276
▼deploy/prometheus.yml
+6-0
6
6
metrics_path: "/api/metrics"
7
7
scrape_interval: 15s
8
8
scrape_timeout: 10s
9
+ # /api/metrics is protected by METRICS_TOKEN when set. Provide the token
10
+ # via environment expansion, e.g. start prometheus with:
11
+ # METRICS_TOKEN=<value> prometheus --config.file=prometheus.yml
12
+ # or mount a file containing the token at /etc/prometheus/och-metrics-token
13
+ authorization:
14
+ credentials_file: /etc/prometheus/och-metrics-token
9
15
static_configs:
10
16
- targets: ["opencodehub-app:4321"]
11
17
labels:
12
18
▼doc/docs-parity-baseline.json
+2-0
1
1
{
2
2
"missingInSite": [
3
+ "cli",
4
+ "getting-started/ssh-keys",
3
5
"support/troubleshooting"
4
6
],
5
7
"missingInDocs": [
6
8
▼docker-compose.nas.yml
+129-67
1
+# OpenCodeHub — NAS / home-server stack
2
+# ============================================================
3
+# Runs everything on a dedicated disk or NAS share by binding every
4
+# data directory to a host path you control (default /mnt/och).
5
+#
6
+# 1. cp .env.example .env
7
+# 2. Set required secrets (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET,
8
+# CRON_SECRET, RUNNER_SECRET, AI_CONFIG_ENCRYPTION_KEY,
9
+# WORKFLOW_SECRET_ENCRYPTION_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD)
10
+# 3. (Optional) point OCH_DATA_DIR at your NAS mount or dedicated disk:
11
+# OCH_DATA_DIR=/mnt/nas/opencodehub # or /volume1/opencodehub on Synology
12
+# 4. docker compose -f docker-compose.nas.yml up -d
13
+#
14
+# Storage backends (local + S3 only):
15
+# - local: STORAGE_TYPE=local, everything under ${OCH_DATA_DIR}/storage
16
+# - s3: STORAGE_TYPE=s3 with STORAGE_ENDPOINT/BUCKET/ACCESS_KEY/SECRET
17
+#
18
+# Optional profiles:
19
+# --profile cloudflare Cloudflare Tunnel for external access
20
+# --profile tailscale Tailscale sidecar
21
+# ============================================================
22
+
23
+x-och-env: &och-env
24
+ NODE_ENV: production
25
+ DATABASE_DRIVER: postgres
26
+ DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
27
+ REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379
28
+ JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env}
29
+ SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env}
30
+ INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env}
31
+ CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env}
32
+ RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env}
33
+ AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env}
34
+ WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env}
35
+ SITE_URL: ${SITE_URL:-http://localhost:4321}
36
+ ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321}
37
+ STORAGE_TYPE: ${STORAGE_TYPE:-local}
38
+ STORAGE_PATH: /data/storage
39
+ GIT_REPOS_PATH: /data/repos
40
+ REPOS_PATH: /data/repos
41
+ GIT_SSH_HOST_KEY: /data/ssh/host_key
42
+ GIT_SSH_PORT: 2222
43
+ RATE_LIMIT_ENABLED: "true"
44
+ RATE_LIMIT_SKIP_DEV: "false"
45
+ CSRF_SKIP_DEV: "false"
46
+ TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1,172.16.0.0/12,100.64.0.0/10}
47
+
48
+x-och-common: &och-common
49
+ image: opencodehub/opencodehub:latest
50
+ restart: unless-stopped
51
+ environment:
52
+ <<: *och-env
53
+ volumes:
54
+ - ${OCH_DATA_DIR:-/mnt/och}/repos:/data/repos
55
+ - ${OCH_DATA_DIR:-/mnt/och}/storage:/data/storage
56
+ - ${OCH_DATA_DIR:-/mnt/och}/ssh:/data/ssh
57
+ - ${OCH_DATA_DIR:-/mnt/och}/cache:/data/cache
58
+ depends_on:
59
+ postgres:
60
+ condition: service_healthy
61
+ redis:
62
+ condition: service_healthy
63
+ networks:
64
+ - opencodehub-network
65
+
1
66
services:
2
- # Main OpenCodeHub application
67
+ # ── Main application (web + API + Git HTTP) ────────────────
3
68
app:
4
- image: swadhinbiswas/opencodehub:latest
69
+ <<: *och-common
5
70
container_name: opencodehub
6
- restart: unless-stopped
71
+ environment:
72
+ <<: *och-env
73
+ PROCESS_TYPE: app
74
+ PORT: 4321
7
75
ports:
8
- - "4321:4321" # Web interface (Internal)
9
- - "2222:2222" # SSH Git (Internal)
76
+ - "${WEB_PORT:-4321}:4321"
77
+ healthcheck:
78
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"]
79
+ interval: 30s
80
+ timeout: 10s
81
+ retries: 3
82
+ start_period: 20s
83
+
84
+ # ── SSH Git server ─────────────────────────────────────────
85
+ ssh-git:
86
+ <<: *och-common
87
+ container_name: opencodehub-ssh
10
88
environment:
11
- - NODE_ENV=production
12
- - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub
13
- - DATABASE_TYPE=postgres
14
- - REDIS_URL=redis://redis:6379
15
- - JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
16
- - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?WORKFLOW_SECRET_ENCRYPTION_KEY is required}
17
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
18
- - SERVER_URL=${SITE_URL:-http://localhost:4321}
19
- - STORAGE_TYPE=local
20
- - STORAGE_PATH=/data/storage
21
- - REPOS_PATH=/data/repos
22
- # TRUSTED_PROXIES allows real IP mapping from Cloudflare or Tailscale sidecars
23
- - TRUSTED_PROXIES=127.0.0.1,172.16.0.0/12,100.64.0.0/10
24
- volumes:
25
- - repos-data:/data/repos
26
- - storage-data:/data/storage
27
- - ssh-data:/data/ssh
28
- - cache-data:/data/cache
29
- depends_on:
30
- postgres:
31
- condition: service_healthy
32
- redis:
33
- condition: service_healthy
34
- networks:
35
- - opencodehub-network
89
+ <<: *och-env
90
+ PROCESS_TYPE: ssh
91
+ ports:
92
+ - "${SSH_PORT:-2222}:2222"
93
+
94
+ # ── Background worker ──────────────────────────────────────
95
+ worker:
96
+ <<: *och-common
97
+ container_name: opencodehub-worker
98
+ environment:
99
+ <<: *och-env
100
+ PROCESS_TYPE: worker
101
+ WORKER_HEALTH_PORT: 9090
102
+ expose:
103
+ - "9090"
104
+ healthcheck:
105
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"]
106
+ interval: 30s
107
+ timeout: 10s
108
+ retries: 3
109
+ start_period: 20s
36
110
37
- # PostgreSQL database
111
+ # ── PostgreSQL ─────────────────────────────────────────────
38
112
postgres:
39
- image: postgres:16-alpine
113
+ image: docker.io/library/postgres:16-alpine
40
114
container_name: opencodehub-postgres
41
115
restart: unless-stopped
42
116
environment:
43
- POSTGRES_USER: opencodehub
44
- POSTGRES_PASSWORD: opencodehub
45
- POSTGRES_DB: opencodehub
117
+ POSTGRES_USER: ${POSTGRES_USER:-opencodehub}
118
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
119
+ POSTGRES_DB: ${POSTGRES_DB:-opencodehub}
46
120
volumes:
47
- - postgres-data:/var/lib/postgresql/data
121
+ - ${OCH_DATA_DIR:-/mnt/och}/postgres:/var/lib/postgresql/data
48
122
networks:
49
123
- opencodehub-network
50
124
healthcheck:
51
- test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"]
125
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"]
52
126
interval: 10s
53
127
timeout: 5s
54
128
retries: 5
55
129
56
- # Redis for caching and sessions
130
+ # ── Redis ──────────────────────────────────────────────────
57
131
redis:
58
- image: redis:7-alpine
132
+ image: docker.io/library/redis:7-alpine
59
133
container_name: opencodehub-redis
60
134
restart: unless-stopped
61
- command: redis-server --appendonly yes
135
+ command:
136
+ - redis-server
137
+ - --requirepass
138
+ - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}
139
+ - --appendonly
140
+ - "yes"
141
+ - --maxmemory
142
+ - 256mb
143
+ - --maxmemory-policy
144
+ - allkeys-lru
62
145
volumes:
63
- - redis-data:/data
146
+ - ${OCH_DATA_DIR:-/mnt/och}/redis:/data
64
147
networks:
65
148
- opencodehub-network
66
149
healthcheck:
67
- test: ["CMD", "redis-cli", "ping"]
150
+ test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
68
151
interval: 10s
69
152
timeout: 5s
70
153
retries: 5
71
154
72
- # -------------------------------------------------------------
73
- # OPTION 1: Cloudflare Tunnel (Uncomment 'profiles' to use)
74
- # Run with: docker-compose -f docker-compose.nas.yml --profile cloudflare up -d
75
- # -------------------------------------------------------------
155
+ # ── Cloudflare Tunnel (optional) ───────────────────────────
76
156
cloudflared:
77
157
image: cloudflare/cloudflared:latest
78
158
container_name: opencodehub-cloudflared
85
165
profiles:
86
166
- cloudflare
87
167
88
- # -------------------------------------------------------------
89
- # OPTION 2: Tailscale (Uncomment 'profiles' to use)
90
- # Run with: docker-compose -f docker-compose.nas.yml --profile tailscale up -d
91
- # -------------------------------------------------------------
168
+ # ── Tailscale (optional) ───────────────────────────────────
92
169
tailscale:
93
170
image: tailscale/tailscale:latest
94
171
container_name: opencodehub-tailscale
95
172
hostname: opencodehub-nas
173
+ restart: unless-stopped
96
174
environment:
97
175
- TS_AUTHKEY=${TS_AUTHKEY:-}
98
176
- TS_STATE_DIR=/var/lib/tailscale
99
177
- TS_USERSPACE=false
100
178
volumes:
101
- - tailscale-data:/var/lib/tailscale
179
+ - ${OCH_DATA_DIR:-/mnt/och}/tailscale:/var/lib/tailscale
102
180
- /dev/net/tun:/dev/net/tun
103
181
cap_add:
104
182
- net_admin
108
186
profiles:
109
187
- tailscale
110
188
111
-volumes:
112
- repos-data:
113
- driver: local
114
- storage-data:
115
- driver: local
116
- ssh-data:
117
- driver: local
118
- cache-data:
119
- driver: local
120
- postgres-data:
121
- driver: local
122
- redis-data:
123
- driver: local
124
- tailscale-data:
125
- driver: local
126
-
127
189
networks:
128
190
opencodehub-network:
129
191
driver: bridge
130
192
▼docker-compose.production.yml
+54-8
49
49
restart: unless-stopped
50
50
expose:
51
51
- "4321" # Internal only — Caddy proxies to this
52
- ports:
53
- - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly)
54
52
environment:
55
53
- NODE_ENV=production
54
+ - PROCESS_TYPE=app
56
55
- DATABASE_DRIVER=postgres
57
56
- DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
58
57
- DATABASE_SSL=false
64
63
- AI_CONFIG_ENCRYPTION_KEY=${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env}
65
64
- WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env}
66
65
- RUNNER_SECRET=${RUNNER_SECRET:?Set RUNNER_SECRET in .env}
67
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
68
66
- SITE_URL=${SITE_URL:-https://opencodehub.example.com}
69
67
- SERVER_URL=${SITE_URL:-https://opencodehub.example.com}
70
68
- STORAGE_TYPE=${STORAGE_TYPE:-local}
77
75
- CSRF_SKIP_DEV=false
78
76
- ALLOWED_ORIGINS=${SITE_URL:-https://opencodehub.example.com}
79
77
- MAX_REQUEST_BODY_MB=${MAX_REQUEST_BODY_MB:-10}
80
- # SSH server (runs inside app container)
81
- - GIT_SSH_PORT=2222
82
- - GIT_SSH_HOST_KEY=/data/ssh/host_key
83
78
volumes:
84
79
- repos-data:/data/repos
85
80
- storage-data:/data/storage
121
116
memory: 512M
122
117
cpus: "0.5"
123
118
119
+ # ── SSH Git Server ─────────────────────────────────────────
120
+ ssh-git:
121
+ build:
122
+ context: .
123
+ dockerfile: Dockerfile
124
+ container_name: opencodehub-ssh
125
+ restart: unless-stopped
126
+ ports:
127
+ - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly)
128
+ environment:
129
+ - NODE_ENV=production
130
+ - PROCESS_TYPE=ssh
131
+ - DATABASE_DRIVER=postgres
132
+ - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
133
+ - REDIS_URL=redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379
134
+ - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env}
135
+ - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env}
136
+ - GIT_REPOS_PATH=/data/repos
137
+ - REPOS_PATH=/data/repos
138
+ - GIT_SSH_PORT=2222
139
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
140
+ volumes:
141
+ - repos-data:/data/repos
142
+ - storage-data:/data/storage
143
+ - ssh-data:/data/ssh
144
+ - cache-data:/data/cache
145
+ depends_on:
146
+ postgres:
147
+ condition: service_healthy
148
+ redis:
149
+ condition: service_healthy
150
+ networks:
151
+ - opencodehub-internal
152
+ - opencodehub-external
153
+ logging:
154
+ driver: json-file
155
+ options:
156
+ max-size: "50m"
157
+ max-file: "5"
158
+ deploy:
159
+ resources:
160
+ limits:
161
+ memory: 512M
162
+ cpus: "0.5"
163
+
124
164
# ── Background Worker ──────────────────────────────────────
125
165
worker:
126
166
build:
128
168
dockerfile: Dockerfile
129
169
container_name: opencodehub-worker
130
170
restart: unless-stopped
131
- command: ["bun", "scripts/worker.ts"]
132
171
expose:
133
172
- "9090" # Worker health check (internal only)
134
173
environment:
135
174
- NODE_ENV=production
175
+ - PROCESS_TYPE=worker
136
176
- DATABASE_DRIVER=postgres
137
177
- DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-opencodehub}
138
178
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379
139
179
- JWT_SECRET=${JWT_SECRET}
180
+ - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET}
181
+ - GIT_REPOS_PATH=/data/repos
182
+ - REPOS_PATH=/data/repos
183
+ - STORAGE_TYPE=${STORAGE_TYPE:-local}
184
+ - STORAGE_PATH=/data/storage
140
185
- LOG_LEVEL=${LOG_LEVEL:-info}
186
+ - WORKER_HEALTH_PORT=9090
141
187
volumes:
142
188
- repos-data:/data/repos
143
189
- storage-data:/data/storage
304
350
command: server /data --console-address ":9001"
305
351
environment:
306
352
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
307
- MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD in .env}
353
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
308
354
volumes:
309
355
- minio-data:/data
310
356
expose:
311
357
▼docker-compose.yml
+210-50
1
+# OpenCodeHub — self-hosted quickstart stack (production-safe)
2
+# ============================================================
3
+# 1. cp .env.example .env
4
+# 2. Fill in ALL required secrets in .env
5
+# 3. docker compose up -d
6
+#
7
+# REQUIRED .env values (compose refuses to start without them):
8
+# JWT_SECRET SESSION_SECRET INTERNAL_HOOK_SECRET CRON_SECRET
9
+# RUNNER_SECRET AI_CONFIG_ENCRYPTION_KEY WORKFLOW_SECRET_ENCRYPTION_KEY
10
+# POSTGRES_PASSWORD REDIS_PASSWORD
11
+# → generate each with: openssl rand -hex 32
12
+#
13
+# Storage — two backends only:
14
+# - local (default): filesystem; point STORAGE_PATH/GIT_REPOS_PATH at a
15
+# dedicated disk or NAS mount for best performance
16
+# - s3: any S3-compatible store (AWS S3, MinIO, R2, Garage, ...) via
17
+# STORAGE_ENDPOINT/STORAGE_BUCKET/STORAGE_ACCESS_KEY_ID/STORAGE_SECRET_ACCESS_KEY
18
+#
19
+# Optional profiles:
20
+# docker compose --profile with-runner up -d # CI/CD runner (needs Docker-in-Docker)
21
+# docker compose --profile with-minio up -d # local S3-compatible storage
22
+# ============================================================
23
+
1
24
services:
2
- # Main OpenCodeHub application
25
+ # ── Main OpenCodeHub application (web + API + Git HTTP) ────
3
26
app:
4
27
build:
5
28
context: .
6
29
dockerfile: Dockerfile
7
- container_name: opencodehub
30
+ container_name: opencodehub-app
8
31
restart: unless-stopped
32
+ env_file: .env
33
+ environment:
34
+ NODE_ENV: production
35
+ PORT: 4321
36
+ PROCESS_TYPE: app
37
+ DATABASE_DRIVER: postgres
38
+ DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
39
+ REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379
40
+ JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env}
41
+ SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env}
42
+ INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env}
43
+ CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env}
44
+ RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env}
45
+ AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env}
46
+ WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env}
47
+ SITE_URL: ${SITE_URL:-http://localhost:4321}
48
+ ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321}
49
+ GIT_REPOS_PATH: /data/repos
50
+ REPOS_PATH: /data/repos
51
+ GIT_SSH_PORT: 2222
52
+ GIT_SSH_HOST_KEY: /data/ssh/host_key
53
+ STORAGE_TYPE: ${STORAGE_TYPE:-local}
54
+ STORAGE_PATH: /data/storage
55
+ RATE_LIMIT_ENABLED: "true"
56
+ RATE_LIMIT_SKIP_DEV: "false"
57
+ CSRF_SKIP_DEV: "false"
9
58
ports:
10
- - "4321:4321" # Web interface
11
- - "2222:2222" # SSH Git
59
+ - "${WEB_PORT:-4321}:4321" # Web UI + API + Git HTTP
60
+ volumes:
61
+ - repos-data:/data/repos
62
+ - storage-data:/data/storage
63
+ - ssh-data:/data/ssh
64
+ - cache-data:/data/cache
65
+ depends_on:
66
+ postgres:
67
+ condition: service_healthy
68
+ redis:
69
+ condition: service_healthy
70
+ networks:
71
+ - och
72
+ healthcheck:
73
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"]
74
+ interval: 30s
75
+ timeout: 10s
76
+ retries: 3
77
+ start_period: 20s
78
+
79
+ # ── SSH Git server ─────────────────────────────────────────
80
+ ssh-git:
81
+ build:
82
+ context: .
83
+ dockerfile: Dockerfile
84
+ container_name: opencodehub-ssh
85
+ restart: unless-stopped
12
86
environment:
13
- - NODE_ENV=production
14
- - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub
15
- - DATABASE_TYPE=postgres
16
- - REDIS_URL=redis://redis:6379
17
- - JWT_SECRET=${JWT_SECRET:-supersecretjwtkey}
18
- - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef}
19
- - SERVER_URL=http://localhost:4321
20
- - DATA_DIR=/data
21
- - STORAGE_TYPE=local
87
+ NODE_ENV: production
88
+ PROCESS_TYPE: ssh
89
+ DATABASE_DRIVER: postgres
90
+ DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
91
+ REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379
92
+ JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env}
93
+ INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env}
94
+ GIT_REPOS_PATH: /data/repos
95
+ REPOS_PATH: /data/repos
96
+ GIT_SSH_PORT: 2222
97
+ GIT_SSH_HOST_KEY: /data/ssh/host_key
98
+ ports:
99
+ - "${SSH_PORT:-2222}:2222" # SSH Git
22
100
volumes:
23
- - ./data/opencodehub:/data
101
+ - repos-data:/data/repos
102
+ - storage-data:/data/storage
103
+ - ssh-data:/data/ssh
104
+ - cache-data:/data/cache
24
105
depends_on:
25
106
postgres:
26
107
condition: service_healthy
27
108
redis:
28
109
condition: service_healthy
29
110
networks:
30
- - opencodehub-network
111
+ - och
112
+
113
+ # ── Background worker (merge queue, webhooks, mirrors, digests) ──
114
+ worker:
115
+ build:
116
+ context: .
117
+ dockerfile: Dockerfile
118
+ container_name: opencodehub-worker
119
+ restart: unless-stopped
120
+ environment:
121
+ NODE_ENV: production
122
+ PROCESS_TYPE: worker
123
+ DATABASE_DRIVER: postgres
124
+ DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub}
125
+ REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379
126
+ JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env}
127
+ INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env}
128
+ GIT_REPOS_PATH: /data/repos
129
+ REPOS_PATH: /data/repos
130
+ STORAGE_TYPE: ${STORAGE_TYPE:-local}
131
+ STORAGE_PATH: /data/storage
132
+ WORKER_HEALTH_PORT: 9090
133
+ expose:
134
+ - "9090" # Worker healthz/ready
135
+ volumes:
136
+ - repos-data:/data/repos
137
+ - storage-data:/data/storage
138
+ - ssh-data:/data/ssh
139
+ - cache-data:/data/cache
140
+ depends_on:
141
+ postgres:
142
+ condition: service_healthy
143
+ redis:
144
+ condition: service_healthy
145
+ networks:
146
+ - och
31
147
healthcheck:
32
- test:
33
- [
34
- "CMD",
35
- "wget",
36
- "--no-verbose",
37
- "--tries=1",
38
- "--spider",
39
- "http://localhost:4321/api/health",
40
- ]
148
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"]
41
149
interval: 30s
42
150
timeout: 10s
43
151
retries: 3
44
- start_period: 10s
152
+ start_period: 20s
45
153
46
- # PostgreSQL database
154
+ # ── PostgreSQL ─────────────────────────────────────────────
47
155
postgres:
48
156
image: docker.io/library/postgres:16-alpine
49
157
container_name: opencodehub-postgres
50
158
restart: unless-stopped
51
159
environment:
52
- POSTGRES_USER: opencodehub
53
- POSTGRES_PASSWORD: opencodehub
54
- POSTGRES_DB: opencodehub
160
+ POSTGRES_USER: ${POSTGRES_USER:-opencodehub}
161
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
162
+ POSTGRES_DB: ${POSTGRES_DB:-opencodehub}
55
163
volumes:
56
- - ./data/postgres:/var/lib/postgresql/data
57
- ports:
58
- - "5432:5432"
164
+ - postgres-data:/var/lib/postgresql/data
59
165
networks:
60
- - opencodehub-network
166
+ - och
61
167
healthcheck:
62
- test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"]
168
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"]
63
169
interval: 10s
64
170
timeout: 5s
65
171
retries: 5
66
172
67
- # Redis for caching and sessions
173
+ # ── Redis (sessions, caching, distributed locking, queues) ──
68
174
redis:
69
175
image: docker.io/library/redis:7-alpine
70
176
container_name: opencodehub-redis
71
177
restart: unless-stopped
72
- command: redis-server --appendonly yes
178
+ command:
179
+ - redis-server
180
+ - --requirepass
181
+ - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}
182
+ - --appendonly
183
+ - "yes"
184
+ - --maxmemory
185
+ - 256mb
186
+ - --maxmemory-policy
187
+ - allkeys-lru
73
188
volumes:
74
- - ./data/redis:/data
75
- ports:
76
- - "6379:6379"
189
+ - redis-data:/data
77
190
networks:
78
- - opencodehub-network
191
+ - och
79
192
healthcheck:
80
- test: ["CMD", "redis-cli", "ping"]
193
+ test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
81
194
interval: 10s
82
195
timeout: 5s
83
196
retries: 5
84
197
85
- # Pipeline runner (for CI/CD jobs)
198
+ # ── CI/CD Runner (Docker-in-Docker, optional) ──────────────
86
199
runner:
87
200
build:
88
201
context: .
89
202
dockerfile: Dockerfile.runner
90
203
container_name: opencodehub-runner
91
204
restart: unless-stopped
92
- privileged: true # Required for Docker-in-Docker
205
+ privileged: true # Required for Docker-in-Docker
93
206
environment:
94
- - RUNNER_TOKEN=${RUNNER_TOKEN:-}
95
- - SERVER_URL=http://app:4321
96
- - DOCKER_HOST=unix:///var/run/docker.sock
207
+ RUNNER_TOKEN: ${RUNNER_TOKEN:-}
208
+ SERVER_URL: http://app:4321
209
+ DOCKER_HOST: unix:///var/run/docker.sock
210
+ RUNNER_WORK_DIR: /work
211
+ RUNNER_CACHE_DIR: /cache
97
212
volumes:
98
213
- /var/run/docker.sock:/var/run/docker.sock
99
- - ./data/runner/work:/work
100
- - ./data/runner/cache:/cache
214
+ - runner-work:/work
215
+ - runner-cache:/cache
101
216
depends_on:
102
217
- app
103
218
networks:
104
- - opencodehub-network
219
+ - och
220
+ profiles:
221
+ - with-runner
222
+
223
+ # ── MinIO (S3-compatible storage, optional) ────────────────
224
+ minio:
225
+ image: minio/minio:latest
226
+ container_name: opencodehub-minio
227
+ restart: unless-stopped
228
+ command: server /data --console-address ":9001"
229
+ environment:
230
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
231
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
232
+ ports:
233
+ - "${MINIO_CONSOLE_PORT:-9001}:9001" # Web console (set 0 to disable)
234
+ volumes:
235
+ - minio-data:/data
236
+ networks:
237
+ - och
238
+ healthcheck:
239
+ test: ["CMD", "mc", "ready", "local"]
240
+ interval: 30s
241
+ timeout: 10s
242
+ retries: 3
243
+ profiles:
244
+ - with-minio
245
+
246
+volumes:
247
+ repos-data:
248
+ driver: local
249
+ storage-data:
250
+ driver: local
251
+ ssh-data:
252
+ driver: local
253
+ cache-data:
254
+ driver: local
255
+ postgres-data:
256
+ driver: local
257
+ redis-data:
258
+ driver: local
259
+ runner-work:
260
+ driver: local
261
+ runner-cache:
262
+ driver: local
263
+ minio-data:
264
+ driver: local
105
265
106
266
networks:
107
- opencodehub-network:
267
+ och:
108
268
driver: bridge
109
269
▼docker-entrypoint.sh
+37-10
5
5
echo "║ OpenCodeHub Starting... ║"
6
6
echo "╚══════════════════════════════════════════╝"
7
7
8
+# ── Process type ──────────────────────────────────────────────
9
+# app (default) — web UI + API + Git HTTP
10
+# ssh — SSH git server (scripts/ssh-server.ts)
11
+# worker — background jobs (scripts/worker.ts)
12
+PROCESS_TYPE="${PROCESS_TYPE:-app}"
13
+
14
+# ── Create required directories ──────────────────────────────
15
+mkdir -p "${GIT_REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}"
16
+mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")"
17
+mkdir -p "${SSH_PATH:-/data/ssh}"
18
+
8
19
# ── Wait for database ────────────────────────────────────────
9
20
if [ -n "$DATABASE_URL" ]; then
10
21
echo "⏳ Waiting for database..."
23
34
sleep 1
24
35
done
25
36
26
- # Run database migrations
37
+ # Run database migrations (deterministic — applies committed migrations in drizzle/)
27
38
echo "🔄 Running database migrations..."
28
39
cd /app
29
- bunx drizzle-kit push --force 2>/dev/null || echo "⚠️ Migration push skipped (may already be up to date)"
40
+ if [ -d "/app/drizzle" ]; then
41
+ bun scripts/migrate.ts
42
+ else
43
+ echo "⚠️ No drizzle/ migrations found — skipping (use 'npm run db:push' in dev)"
44
+ fi
30
45
echo "✅ Migrations complete"
31
46
fi
32
47
33
48
# ── Initialize SSH host key ──────────────────────────────────
34
-if [ -n "$SSH_HOST_KEY_PATH" ] && [ ! -f "$SSH_HOST_KEY_PATH" ]; then
49
+HOST_KEY="${GIT_SSH_HOST_KEY:-${SSH_HOST_KEY_PATH:-/data/ssh/host_key}}"
50
+if [ -n "$HOST_KEY" ] && [ ! -f "$HOST_KEY" ]; then
35
51
echo "🔑 Generating SSH host key..."
36
- ssh-keygen -t ed25519 -f "$SSH_HOST_KEY_PATH" -N "" -q
52
+ mkdir -p "$(dirname "$HOST_KEY")"
53
+ ssh-keygen -t ed25519 -f "$HOST_KEY" -N "" -q
37
54
echo "✅ SSH host key generated"
38
55
fi
39
56
40
-# ── Create required directories ──────────────────────────────
41
-mkdir -p "${REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}"
42
-mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")"
43
-
44
-echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..."
45
-exec bun ./dist/server/entry.mjs
57
+# ── Start the requested process ──────────────────────────────
58
+cd /app
59
+case "$PROCESS_TYPE" in
60
+ ssh)
61
+ echo "🚀 Starting OpenCodeHub SSH Git server on port ${GIT_SSH_PORT:-2222}..."
62
+ exec bun scripts/ssh-server.ts
63
+ ;;
64
+ worker)
65
+ echo "🚀 Starting OpenCodeHub background worker..."
66
+ exec bun scripts/worker.ts
67
+ ;;
68
+ *)
69
+ echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..."
70
+ exec bun ./dist/server/entry.mjs
71
+ ;;
72
+esac
46
73
▼docs-site/README.md
+42-35
1
-# Starlight Starter Kit: Basics
1
+# OpenCodeHub Documentation Site
2
2
3
-[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build)
3
+The official documentation for [OpenCodeHub](https://github.com/swadhinbiswas/OpencodeHub) — an open-source, self-hosted Git platform with Stacked PRs, AI Code Review, and Smart Merge Queues. Built with [Astro Starlight](https://starlight.astro.build) and deployed to [Cloudflare Workers](https://workers.cloudflare.com).
4
4
5
-```
6
-bun create astro@latest -- --template starlight
7
-```
5
+**Live site:** <https://docs.opencodehub.space>
8
6
9
-> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
7
+## Project Structure
10
8
11
-## 🚀 Project Structure
12
9
13
-Inside of your Astro + Starlight project, you'll see the following folders and files:
14
10
15
-```
16
-.
17
-├── public/
18
-├── src/
19
-│ ├── assets/
20
-│ ├── content/
21
-│ │ └── docs/
22
-│ └── content.config.ts
23
-├── astro.config.mjs
24
-├── package.json
25
-└── tsconfig.json
26
-```
11
+Starlight looks for `.md` or `.mdx` files in `src/content/docs/`. Each file is exposed as a route based on its file name.
12
+
13
+Images go in `src/assets/` and are embedded in Markdown with relative links. Static assets like favicons go in `public/`.
14
+
15
+## Commands
27
16
28
-Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
17
+All commands run from the project root:
29
18
30
-Images can be added to `src/assets/` and embedded in Markdown with a relative link.
19
+| Command | Action |
20
+| :-------------------- | :------------------------------------------------ |
21
+| `bun install` | Install dependencies |
22
+| `bun dev` | Start local dev server at `localhost:4321` |
23
+| `bun build` | Build production site to `./dist/` |
24
+| `bun preview` | Preview the production build locally |
25
+| `bun deploy` | Deploy to Cloudflare Workers via Wrangler |
26
+| `bun astro ...` | Run CLI commands like `astro add`, `astro check` |
27
+| `bun astro -- --help` | Get help using the Astro CLI |
31
28
32
-Static assets, like favicons, can be placed in the `public/` directory.
29
+## Development
33
30
34
-## 🧞 Commands
31
+```bash
32
+# Install dependencies
33
+bun install
35
34
36
-All commands are run from the root of the project, from a terminal:
35
+# Start dev server
36
+bun dev
37
+```
38
+
39
+The dev server runs at `http://localhost:4321`. Edit files in `src/content/docs/` and the site hot-reloads.
40
+
41
+## Deployment
42
+
43
+This site is deployed to Cloudflare Workers. To deploy:
44
+
45
+```bash
46
+bun build
47
+bun deploy
48
+```
37
49
38
-| Command | Action |
39
-| :------------------------ | :----------------------------------------------- |
40
-| `bun install` | Installs dependencies |
41
-| `bun dev` | Starts local dev server at `localhost:4321` |
42
-| `bun build` | Build your production site to `./dist/` |
43
-| `bun preview` | Preview your build locally, before deploying |
44
-| `bun astro ...` | Run CLI commands like `astro add`, `astro check` |
45
-| `bun astro -- --help` | Get help using the Astro CLI |
50
+The `wrangler.jsonc` configures the worker to serve the static `dist/` directory. See [Cloudflare deployment guide](https://docs.opencodehub.space/administration/deploy-cloudflare/) for details.
46
51
47
-## 👀 Want to learn more?
52
+## Tech Stack
48
53
49
-Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
54
+- **Framework:** [Astro](https://astro.build) with [Starlight](https://starlight.astro.build) documentation theme
55
+- **Deployment:** [Cloudflare Workers](https://workers.cloudflare.com) (static assets)
56
+- **Package Manager:** [Bun](https://bun.sh)
50
57
▼docs-site/astro.config.mjs
+36-3
4
4
5
5
// https://astro.build/config
6
6
export default defineConfig({
7
+ site: "https://docs.opencodehub.space",
7
8
integrations: [
8
9
starlight({
9
10
title: "OpenCodeHub Docs",
42
43
{ label: "Branch Protection", slug: "guides/branch-protection" },
43
44
{ label: "Webhooks", slug: "guides/webhooks" },
44
45
{ label: "Storage Adapters", slug: "guides/storage-adapters" },
46
+ { label: "CircleCI Integration", slug: "guides/circleci-integration" },
45
47
],
46
48
},
47
49
{
65
67
{ label: "Stacked PRs", slug: "features/stacked-prs" },
66
68
{ label: "AI Review", slug: "features/ai-review" },
67
69
{ label: "Merge Queue", slug: "features/merge-queue" },
70
+ { label: "CI/CD Actions", slug: "features/ci-actions" },
71
+ { label: "CI/CD Pipelines", slug: "features/ci-cd" },
68
72
{ label: "Automation Rules", slug: "features/automations" },
69
73
{ label: "PR Inbox", slug: "features/inbox" },
70
74
{ label: "Developer Metrics", slug: "features/developer-metrics" },
71
75
{ label: "Notifications", slug: "features/notifications" },
72
- { label: "CI/CD Actions", slug: "features/ci-actions" },
76
+ { label: "Slack Integration", slug: "features/slack-integration" },
73
77
{ label: "CLI Workflows", slug: "features/cli" },
74
78
],
75
79
},
86
90
{ label: "cPanel", slug: "administration/deploy-cpanel" },
87
91
{ label: "CyberPanel", slug: "administration/deploy-cyberpanel" },
88
92
{ label: "Cloudflare", slug: "administration/deploy-cloudflare" },
93
+ { label: "Coolify", slug: "administration/deploy-coolify" },
94
+ { label: "NAS (Synology/TrueNAS)", slug: "administration/deploy-nas" },
95
+ { label: "Kubernetes (Helm)", slug: "administration/kubernetes" },
89
96
],
90
97
},
91
98
{
95
102
{ label: "Configuration", slug: "administration/configuration" },
96
103
{ label: "Monitoring", slug: "administration/monitoring" },
97
104
{ label: "Security", slug: "administration/security" },
105
+ { label: "Deployment Matrix", slug: "administration/deployment-matrix" },
106
+ { label: "Secret Rotation", slug: "administration/secret-rotation" },
107
+ { label: "Expose NAS / Home Server", slug: "administration/expose-nas" },
108
+ { label: "CircleCI Cloud Workers", slug: "administration/circleci-cloud" },
109
+ { label: "CircleCI Self-Hosted", slug: "administration/circleci-worker" },
110
+ ],
111
+ },
112
+ {
113
+ label: "Operations",
114
+ items: [
115
+ { label: "Incident Runbook", slug: "administration/incident-runbook" },
116
+ { label: "Postmortem Template", slug: "administration/postmortem-template" },
117
+ { label: "Operations Drills", slug: "administration/operations-drills" },
98
118
],
99
119
},
100
120
{
103
123
},
104
124
{
105
125
label: "Development",
106
- autogenerate: { directory: "development" },
126
+ items: [
127
+ { label: "Architecture", slug: "development/architecture" },
128
+ { label: "Local Dev Setup", slug: "development/local-dev-setup" },
129
+ { label: "Contributing", slug: "development/contributing" },
130
+ { label: "Database Schema", slug: "development/database-schema" },
131
+ { label: "Database Migrations", slug: "development/database-migrations" },
132
+ { label: "Testing", slug: "development/testing" },
133
+ { label: "Testing & CI", slug: "development/testing-pipelines" },
134
+ { label: "Security Hardening", slug: "development/security-hardening" },
135
+ { label: "Observability", slug: "development/observability" },
136
+ { label: "Publishing CLI", slug: "development/publishing-cli" },
137
+ ],
107
138
},
108
139
{
109
140
label: "Reference",
110
- autogenerate: { directory: "reference" },
141
+ items: [
142
+ { label: "Glossary", slug: "reference/glossary" },
143
+ ],
111
144
},
112
145
],
113
146
}),
114
147
▼docs-site/src/content/docs/FREE-DEPLOYMENT.md
+399-0
1
+---
2
+title: "Free Deployment Options"
3
+---
4
+
5
+# OpenCodeHub — Free Deployment Options
6
+
7
+> Deploy OpenCodeHub for free with persistent file storage for git repositories.
8
+
9
+---
10
+
11
+## Quick Comparison
12
+
13
+| Platform | Cost | Storage | RAM | CPU | Best For |
14
+|----------|------|---------|-----|-----|----------|
15
+| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall |
16
+| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused |
17
+| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing |
18
+| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage |
19
+| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo |
20
+| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid |
21
+
22
+---
23
+
24
+## Option 1: Oracle Cloud Free Tier (Recommended)
25
+
26
+**Always Free** — No credit card charge, no time limit.
27
+
28
+### What You Get
29
+- 4 ARM cores (Ampere A1)
30
+- 24 GB RAM
31
+- 200 GB boot volume
32
+- 2 x 1TB block volumes (total 2TB)
33
+- Free PostgreSQL database
34
+- Free Redis
35
+
36
+### Steps
37
+
38
+```bash
39
+# 1. Sign up at cloud.oracle.com
40
+# 2. Create a VM instance:
41
+# - Name: opencodehub
42
+# - Image: Ubuntu 22.04
43
+# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM)
44
+# - Upload SSH key
45
+# - Create boot volume: 100 GB
46
+
47
+# 3. SSH into your instance
48
+ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP
49
+
50
+# 4. Install Docker
51
+curl -fsSL https://get.docker.com -o get-docker.sh
52
+sudo sh get-docker.sh
53
+sudo usermod -aG docker $USER
54
+
55
+# 5. Install Docker Compose
56
+sudo apt install docker-compose-plugin -y
57
+
58
+# 6. Clone OpenCodeHub
59
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
60
+cd OpencodeHub
61
+
62
+# 7. Configure
63
+cp .env.example .env
64
+nano .env # Edit secrets
65
+
66
+# 8. Start
67
+sudo docker compose up -d
68
+
69
+# 9. Create admin
70
+sudo docker compose exec app bun run scripts/seed-admin.ts
71
+```
72
+
73
+### Access Your Instance
74
+```
75
+Web: http://YOUR_PUBLIC_IP:4321
76
+SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git
77
+HTTPS: Set up Nginx + Let's Encrypt (free)
78
+```
79
+
80
+---
81
+
82
+## Option 2: Home Server / NAS
83
+
84
+**$0 forever** — Use hardware you already own.
85
+
86
+### What You Need
87
+- Any computer running Linux/Mac/Windows
88
+- 2GB+ RAM recommended
89
+- 20GB+ free disk space
90
+
91
+### Quick Start (Linux/Mac)
92
+
93
+```bash
94
+# Install Docker
95
+curl -fsSL https://get.docker.com | sh
96
+
97
+# Clone and start
98
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
99
+cd OpencodeHub
100
+cp .env.example .env
101
+nano .env # Set secrets
102
+docker compose up -d
103
+
104
+# Create admin
105
+docker compose exec app bun run scripts/seed-admin.ts
106
+```
107
+
108
+### NAS Deployment
109
+
110
+**Synology / TrueNAS / QNAP:**
111
+1. Install Docker from Package Center
112
+2. Use Docker Compose or Portainer
113
+3. Mount a shared folder for `/data`
114
+
115
+### Access from Anywhere
116
+- **Tailscale** (free): `tailscale up` → access via Tailscale IP
117
+- **Cloudflare Tunnel** (free): Expose without opening ports
118
+- **Port forwarding**: Open port 4321 on your router
119
+
120
+---
121
+
122
+## Option 3: Fly.io Free Tier
123
+
124
+**Free** — 3GB persistent volume included.
125
+
126
+### Steps
127
+
128
+```bash
129
+# 1. Install flyctl
130
+curl -L https://fly.io/install.sh | sh
131
+
132
+# 2. Login
133
+fly auth login
134
+
135
+# 3. Launch
136
+fly launch --name opencodehub
137
+
138
+# 4. Add persistent volume
139
+fly volume create opencodehub_data --region sjc --size 3
140
+
141
+# 5. Set secrets
142
+fly secrets set JWT_SECRET=$(openssl rand -hex 32)
143
+fly secrets set SESSION_SECRET=$(openssl rand -hex 32)
144
+fly secrets set DATABASE_URL="postgresql://..."
145
+fly secrets set REDIS_URL="redis://..."
146
+
147
+# 6. Deploy
148
+fly deploy
149
+```
150
+
151
+### Limitations
152
+- 3GB storage (enough for small repos)
153
+- 256MB RAM (may be slow for large repos)
154
+- Apps休眠 after inactivity (cold starts take ~30s)
155
+
156
+---
157
+
158
+## Option 4: Google Cloud Free Tier
159
+
160
+**Always Free** — e2-micro instance.
161
+
162
+### Steps
163
+
164
+```bash
165
+# 1. Sign up at cloud.google.com
166
+# 2. Create e2-micro VM:
167
+# - Machine type: e2-micro (0.25 vCPU, 1GB RAM)
168
+# - Boot disk: 30GB SSD (free)
169
+# - Firewall: Allow HTTP (80), SSH (22)
170
+
171
+# 3. SSH in and install Docker
172
+ssh -i ~/.ssh/key USER@EXTERNAL_IP
173
+sudo apt update && sudo apt install docker.io docker-compose-plugin -y
174
+
175
+# 4. Deploy OpenCodeHub
176
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
177
+cd OpencodeHub
178
+cp .env.example .env
179
+nano .env
180
+docker compose up -d
181
+```
182
+
183
+### Limitations
184
+- Only 1GB RAM (tight for production)
185
+- 0.25 vCPU (slow builds)
186
+- Good for personal/light use
187
+
188
+---
189
+
190
+## Option 5: Self-Hosted on Existing VPS
191
+
192
+If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.):
193
+
194
+```bash
195
+# SSH into your server
196
+ssh root@YOUR_SERVER_IP
197
+
198
+# One-line install
199
+curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash
200
+```
201
+
202
+---
203
+
204
+## Option 6: Docker on Windows/Mac
205
+
206
+For local development or personal use:
207
+
208
+```bash
209
+# Install Docker Desktop for Windows/Mac
210
+# https://docs.docker.com/get-docker/
211
+
212
+# Clone and start
213
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
214
+cd OpencodeHub
215
+cp .env.example .env
216
+nano .env
217
+docker compose up -d
218
+
219
+# Create admin
220
+docker compose exec app bun run scripts/seed-admin.ts
221
+```
222
+
223
+---
224
+
225
+## Storage Configuration
226
+
227
+### Local Storage (Default)
228
+```bash
229
+STORAGE_TYPE=local
230
+STORAGE_PATH=/data/storage
231
+GIT_REPOS_PATH=/data/repositories
232
+```
233
+
234
+### S3-Compatible (MinIO on same server)
235
+```bash
236
+# Add to docker-compose.yml:
237
+minio:
238
+ image: minio/minio
239
+ command: server /data --console-address ":9001"
240
+ volumes:
241
+ - ./data/minio:/data
242
+ ports:
243
+ - "9001:9001"
244
+
245
+STORAGE_TYPE=s3
246
+STORAGE_ENDPOINT=http://minio:9000
247
+STORAGE_BUCKET=opencodehub
248
+STORAGE_ACCESS_KEY_ID=minioadmin
249
+STORAGE_SECRET_ACCESS_KEY=minioadmin
250
+```
251
+
252
+---
253
+
254
+## HTTPS Setup (Free)
255
+
256
+### With Let's Encrypt + Nginx
257
+
258
+```bash
259
+# Install Nginx and Certbot
260
+sudo apt install nginx certbot python3-certbot-nginx -y
261
+
262
+# Create Nginx config
263
+sudo nano /etc/nginx/sites-available/opencodehub
264
+```
265
+
266
+```nginx
267
+server {
268
+ listen 80;
269
+ server_name git.yourdomain.com;
270
+ return 301 https://$host$request_uri;
271
+}
272
+
273
+server {
274
+ listen 443 ssl http2;
275
+ server_name git.yourdomain.com;
276
+
277
+ ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
278
+ ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
279
+
280
+ client_max_body_size 500M;
281
+
282
+ location / {
283
+ proxy_pass http://localhost:4321;
284
+ proxy_set_header Host $host;
285
+ proxy_set_header X-Real-IP $remote_addr;
286
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
287
+ proxy_set_header X-Forwarded-Proto $scheme;
288
+ }
289
+
290
+ # WebSocket support
291
+ location /api/realtime {
292
+ proxy_pass http://localhost:4321;
293
+ proxy_http_version 1.1;
294
+ proxy_set_header Upgrade $http_upgrade;
295
+ proxy_set_header Connection "upgrade";
296
+ }
297
+}
298
+```
299
+
300
+```bash
301
+# Enable and get SSL
302
+sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/
303
+sudo certbot --nginx -d git.yourdomain.com
304
+```
305
+
306
+---
307
+
308
+## Environment Variables (Production)
309
+
310
+```bash
311
+# Generate all secrets
312
+JWT_SECRET=$(openssl rand -hex 32)
313
+SESSION_SECRET=$(openssl rand -hex 32)
314
+INTERNAL_HOOK_SECRET=$(openssl rand -hex 32)
315
+CRON_SECRET=$(openssl rand -hex 32)
316
+RUNNER_SECRET=$(openssl rand -hex 32)
317
+AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32)
318
+WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32)
319
+
320
+# Your .env file
321
+DATABASE_DRIVER=postgres
322
+DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub
323
+DATABASE_SSL=false
324
+REDIS_URL=redis://localhost:6379
325
+SITE_URL=https://git.yourdomain.com
326
+STORAGE_TYPE=local
327
+GIT_REPOS_PATH=/data/repositories
328
+NODE_ENV=production
329
+```
330
+
331
+---
332
+
333
+## Backup Strategy
334
+
335
+### Automated Backup Script
336
+```bash
337
+#!/bin/bash
338
+# backup.sh — Run daily via cron
339
+BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
340
+mkdir -p "$BACKUP_DIR"
341
+
342
+# Database dump
343
+docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql"
344
+
345
+# Git repos
346
+tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/
347
+
348
+# Upload to S3 (optional)
349
+# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/
350
+```
351
+
352
+### Cron Job
353
+```bash
354
+# Add to crontab
355
+crontab -e
356
+0 2 * * * /path/to/backup.sh
357
+```
358
+
359
+---
360
+
361
+## Monitoring
362
+
363
+### Health Check
364
+```bash
365
+# Check if app is running
366
+curl -f http://localhost:4321/api/health || echo "DOWN"
367
+
368
+# Check Docker status
369
+docker compose ps
370
+docker compose logs --tail=50 app
371
+```
372
+
373
+### Uptime Robot (Free)
374
+1. Sign up at uptimerobot.com
375
+2. Add HTTP monitor: `http://YOUR_IP:4321/api/health`
376
+3. Get notified if your instance goes down
377
+
378
+---
379
+
380
+## Recommended Free Setup
381
+
382
+For a **personal Git server** that stores all your code:
383
+
384
+| Component | Choice |
385
+|-----------|--------|
386
+| **Cloud** | Oracle Cloud Free Tier |
387
+| **Server** | ARM A1 (4 cores, 24GB RAM) |
388
+| **Storage** | 100GB boot + 1TB block volume |
389
+| **Database** | PostgreSQL (Docker) |
390
+| **Cache** | Redis (Docker) |
391
+| **HTTPS** | Nginx + Let's Encrypt |
392
+| **Access** | Tailscale (private) or port forwarding |
393
+| **Backups** | Cron script + local S3 |
394
+
395
+**Total cost: $0/month forever.**
396
+
397
+---
398
+
399
+*Generated for OpenCodeHub free deployment.*
0
400
▼docs-site/src/content/docs/RENDER-DEPLOYMENT.md
+167-0
1
+---
2
+title: "Render Deployment Guide"
3
+---
4
+
5
+# OpenCodeHub — Render Deployment Guide
6
+
7
+> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month.
8
+
9
+---
10
+
11
+## What You Get
12
+
13
+- OpenCodeHub running in Asia (Singapore)
14
+- Free PostgreSQL database
15
+- Free Redis via Upstash
16
+- 1GB persistent storage for git repos
17
+- HTTPS included
18
+- Admin account
19
+
20
+---
21
+
22
+## Step 1: Create Upstash Redis (2 min)
23
+
24
+1. Go to **https://upstash.com** → Sign up (free)
25
+2. Click **Create Database**
26
+3. Settings:
27
+ - **Name**: `opencodehub`
28
+ - **Region**: **Asia Pacific (Singapore)**
29
+ - **Plan**: Pay as you go (free tier: 10K commands/day)
30
+4. Click **Create**
31
+5. Copy the **Redis URL** (looks like `rediss://...@...`)
32
+6. Keep this tab open — you'll need it in Step 3
33
+
34
+---
35
+
36
+## Step 2: Deploy to Render (5 min)
37
+
38
+1. Go to **https://render.com** → Sign up (free)
39
+2. Click **New** → **Blueprint**
40
+3. Connect your GitHub account
41
+4. Select your OpenCodeHub repository
42
+5. Render detects `render.yaml` and shows:
43
+ - `opencodehub-db` (PostgreSQL — Singapore)
44
+ - `opencodehub` (Web Service — Singapore)
45
+6. Click **Apply**
46
+7. Wait for deployment (~3-5 min first time)
47
+
48
+---
49
+
50
+## Step 3: Set Redis URL (1 min)
51
+
52
+1. Go to your Web Service → **Environment** tab
53
+2. Find `REDIS_URL` (shows "Not set")
54
+3. Click the pencil icon → Paste your **Upstash Redis URL**
55
+4. Click **Save**
56
+
57
+---
58
+
59
+## Step 4: Set Site URL (1 min)
60
+
61
+1. In the same **Environment** tab
62
+2. Find `SITE_URL` (shows "Not set")
63
+3. Click the pencil icon → Enter your Render URL:
64
+ ```
65
+ https://opencodehub.onrender.com
66
+ ```
67
+ (Replace with your actual URL from the service overview)
68
+4. Click **Save**
69
+
70
+---
71
+
72
+## Step 5: Manual Deploy
73
+
74
+1. Go to your Web Service → **Manual Deploy**
75
+2. Click **Deploy latest commit**
76
+3. Wait for build to complete (~2-3 min)
77
+4. Click the **URL** at the top to open your instance
78
+
79
+---
80
+
81
+## Step 6: Create Admin Account
82
+
83
+You'll see the **Setup Wizard**. Fill in:
84
+
85
+| Field | Value |
86
+|-------|-------|
87
+| Username | `admin` (or your choice) |
88
+| Email | Your email |
89
+| Password | Strong password |
90
+
91
+Click **Create Account** — you're now logged in as admin!
92
+
93
+---
94
+
95
+## Step 7: Test It
96
+
97
+```bash
98
+# Create a repo via web UI, then clone:
99
+git clone https://opencodehub.onrender.com/your-username/my-repo.git
100
+
101
+# Push code:
102
+cd my-repo
103
+echo "# Hello" > README.md
104
+git add . && git commit -m "Initial"
105
+git push origin main
106
+```
107
+
108
+---
109
+
110
+## Done! Summary
111
+
112
+| Component | Service | Region | Cost |
113
+|-----------|---------|--------|------|
114
+| App | Render Free | Singapore | $0 |
115
+| PostgreSQL | Render Free | Singapore | $0 |
116
+| Redis | Upstash Free | Singapore | $0 |
117
+| Storage | Render Disk 1GB | Singapore | $0 |
118
+| **Total** | | | **$0/month** |
119
+
120
+---
121
+
122
+## Keep Alive (Optional)
123
+
124
+Render free tier spins down after 15min inactivity. To prevent:
125
+
126
+1. Go to **https://uptimerobot.com** → Sign up (free)
127
+2. Add Monitor → Type: HTTP(s)
128
+3. URL: `https://opencodehub.onrender.com/api/health`
129
+4. Monitoring interval: 5 minutes
130
+5. Save — your app stays awake
131
+
132
+---
133
+
134
+## Upgrade Path (When You Grow)
135
+
136
+| Need | Upgrade | Cost |
137
+|------|---------|------|
138
+| Always-on (no spin-down) | Render Starter | $7/mo |
139
+| More storage (10GB+) | Render Disk upgrade | $1-7/mo |
140
+| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K |
141
+| PostgreSQL expiry | Render Starter DB | $7/mo |
142
+
143
+---
144
+
145
+## Troubleshooting
146
+
147
+**App won't start?**
148
+- Check **Logs** tab in Render
149
+- Verify `REDIS_URL` is set correctly
150
+- Ensure `SITE_URL` matches your Render URL
151
+
152
+**Redis connection failed?**
153
+- Verify Redis URL from Upstash (starts with `rediss://`)
154
+- Check Upstash dashboard shows "Active"
155
+- Ensure region is Singapore
156
+
157
+**Git clone fails?**
158
+- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git`
159
+- SSH not available on Render free tier (use HTTPS)
160
+
161
+**Slow first request?**
162
+- Normal for free tier (cold start ~30s)
163
+- Add UptimeRobot ping to keep it warm
164
+
165
+---
166
+
167
+*OpenCodeHub — Deployed on Render, Asia Region, $0/month.*
0
168
▼docs-site/src/content/docs/administration/air-gapped.md
+33-0
1
+# Air-gapped Environments
2
+
3
+OpenCodeHub can be deployed in fully offline, air-gapped networks.
4
+
5
+## Prerequisites
6
+- A local container registry to host the OpenCodeHub Docker image.
7
+- A local NPM/mirror registry if you plan to build from source.
8
+
9
+## Deployment Steps
10
+1. On a machine with internet access, pull the required images:
11
+ ```bash
12
+ docker pull ghcr.io/swadhinbiswas/opencodehub:latest
13
+ docker pull postgres:15-alpine
14
+ docker pull redis:7-alpine
15
+ ```
16
+2. Save the images to tar archives:
17
+ ```bash
18
+ docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar
19
+ docker save postgres:15-alpine -o pg.tar
20
+ docker save redis:7-alpine -o redis.tar
21
+ ```
22
+3. Transfer the archives to the air-gapped network via secure media.
23
+4. Load the images on the target machine:
24
+ ```bash
25
+ docker load -i och.tar
26
+ docker load -i pg.tar
27
+ docker load -i redis.tar
28
+ ```
29
+
30
+## Configuration Adjustments
31
+- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider.
32
+- Disable outbound webhooks pointing to external SaaS platforms.
33
+- Ensure the AI Code Review module is configured to use a `local` provider (e.g., an internal Ollama instance) rather than cloud APIs like OpenAI.
0
34
▼docs-site/src/content/docs/administration/backup-restore.md
+29-0
1
+# Backup and Restore
2
+
3
+This guide covers how to back up and restore an OpenCodeHub instance.
4
+
5
+## Backup Strategy
6
+
7
+### 1. Database
8
+For PostgreSQL, use `pg_dump`:
9
+```bash
10
+pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql
11
+```
12
+
13
+### 2. Git Repositories
14
+The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`).
15
+You can back this up using `rsync` or `tar`:
16
+```bash
17
+tar -czvf repos_backup.tar.gz ./data/repos/
18
+```
19
+
20
+### 3. Object Storage
21
+If you are using S3 or another object storage provider for artifacts, ensure you have enabled versioning and standard bucket backups through your cloud provider. If using local storage, back up the configured `./data/storage` directory.
22
+
23
+## Restore Strategy
24
+To restore:
25
+1. Stop the application and worker processes.
26
+2. Restore the database using `psql`.
27
+3. Extract the Git repositories to `GIT_REPOS_PATH`.
28
+4. Restore object storage data.
29
+5. Restart the application.
0
30
▼docs-site/src/content/docs/administration/configuration.md
+187-36
2
2
title: "Configuration Reference"
3
3
---
4
4
5
+OpenCodeHub is configured via environment variables. Copy `.env.example` to `.env` and edit.
5
6
6
-OpenCodeHub is configured exclusively via environment variables.
7
+## Server
7
8
8
-## Core
9
+| Variable | Description | Default |
10
+|----------|-------------|---------|
11
+| `HOST` | Bind address | `0.0.0.0` |
12
+| `PORT` | HTTP port | `4321` |
13
+| `SITE_URL` | Public URL of the instance | `http://localhost:4321` |
14
+| `APP_NAME` | Instance name | `OpenCodeHub` |
15
+| `NODE_ENV` | `development` or `production` | `development` |
9
16
10
-| Variable | Description | Required | Default |
11
-|----------|-------------|----------|---------|
12
-| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` |
13
-| `PORT` | Port to listen on | No | `3000` |
14
-| `NODE_ENV` | `development` or `production` | No | `development` |
17
+## Database
15
18
16
-## Security (Critical)
19
+| Variable | Description | Default |
20
+|----------|-------------|---------|
21
+| `DATABASE_DRIVER` | `postgres`, `sqlite`, or `turso` (LibSQL) | `postgres` |
22
+| `DATABASE_URL` | Connection string | — |
23
+| `DATABASE_POOL_SIZE` | Connection pool size | `10` |
24
+| `DATABASE_SSL` | Enable PostgreSQL SSL | `false` |
25
+| `DATABASE_AUTH_TOKEN` | Auth token (Turso/LibSQL only) | — |
26
+
27
+:::caution[Production]
28
+PostgreSQL is **required** for production. All schemas use `pgTable`. SQLite is for development only.
29
+:::
30
+
31
+## Redis
32
+
33
+| Variable | Description | Default |
34
+|----------|-------------|---------|
35
+| `REDIS_URL` | Redis connection string | `redis://localhost:6379` |
36
+| `REDIS_PASSWORD` | Redis password | — |
37
+
38
+Redis is required in production for distributed locking, rate limiting, and sessions. For edge deployments (Vercel), use Upstash Redis with `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`.
39
+
40
+## Authentication
17
41
18
42
| Variable | Description | Generate With |
19
43
|----------|-------------|---------------|
20
-| `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` |
21
-| `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` |
22
-| `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` |
44
+| `JWT_SECRET` | Signs auth tokens | `openssl rand -base64 32` |
45
+| `JWT_EXPIRES_IN` | Token expiry | `7d` |
46
+| `SESSION_SECRET` | Signs session cookies | `openssl rand -base64 32` |
23
47
24
-## Database
48
+## Security Secrets
25
49
26
-| Variable | Description | Example |
50
+All secrets below must be unique. Generate each with `openssl rand -hex 32`.
51
+
52
+| Variable | Purpose |
53
+|----------|---------|
54
+| `INTERNAL_HOOK_SECRET` | Secures git hook communication between Git server and app |
55
+| `CRON_SECRET` | Protects cron endpoints (GC, mirror sync, notification digests) |
56
+| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider API keys at rest |
57
+| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts CI/CD workflow secrets at rest |
58
+| `RUNNER_SECRET` | Shared secret between server and CI runners |
59
+| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public |
60
+
61
+## OAuth Providers
62
+
63
+| Variable | Description |
64
+|----------|-------------|
65
+| `OAUTH_GOOGLE_CLIENT_ID` | Google OAuth client ID |
66
+| `OAUTH_GOOGLE_CLIENT_SECRET` | Google OAuth client secret |
67
+| `OAUTH_GITHUB_CLIENT_ID` | GitHub OAuth client ID |
68
+| `OAUTH_GITHUB_CLIENT_SECRET` | GitHub OAuth client secret |
69
+| `OAUTH_GITLAB_CLIENT_ID` | GitLab OAuth client ID |
70
+| `OAUTH_GITLAB_CLIENT_SECRET` | GitLab OAuth client secret |
71
+
72
+OpenCodeHub also supports SSO via SAML and OIDC. Configure these through the admin dashboard.
73
+
74
+## Git Configuration
75
+
76
+| Variable | Description | Default |
27
77
|----------|-------------|---------|
28
-| `DATABASE_DRIVER` | `postgres`, `mysql`, `sqlite`, `libsql` | `postgres` |
29
-| `DATABASE_URL` | Connection string | `postgresql://user:pass@host:5432/db` |
30
-| `DATABASE_AUTH_TOKEN`| Auth token (Turso/LibSQL only) | |
78
+| `GIT_REPOS_PATH` | Directory for git repositories | `./data/repositories` |
79
+| `GIT_SSH_PORT` | SSH git server port | `2222` |
80
+| `GIT_SSH_HOST_KEY` | Path to SSH host key | `./data/ssh/host_key` |
31
81
32
82
## Storage
33
83
34
84
| Variable | Description | Default |
35
85
|----------|-------------|---------|
36
-| `STORAGE_TYPE` | `local` (filesystem) or `s3` (any S3-compatible object store) | `local` |
37
-| `STORAGE_PATH` | Path for local storage | `./data/storage` |
38
-| `STORAGE_BUCKET` | Bucket name (required for `s3`) | |
39
-| `STORAGE_REGION` | S3 region | `us-east-1` |
40
-| `STORAGE_ENDPOINT` | Custom S3 endpoint (MinIO, R2, Garage, SeaweedFS, Ceph RGW, etc.) | |
41
-| `STORAGE_ACCESS_KEY_ID` | S3 access key | |
42
-| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key | |
86
+| `STORAGE_TYPE` | `local` or `s3` | `local` |
87
+| `STORAGE_PATH` | Local storage path | `./data/storage` |
43
88
44
-> Only `local` and `s3` storage backends are supported. Previous releases
45
-> also supported `gdrive`, `azure`, `gcs`, `dropbox`, `onedrive`, `ftp`, and
46
-> an rclone-as-adapter option; these have been removed in favour of
47
-> S3-compatible object storage. See `docs/guides/storage-adapters.md`.
89
+### S3-Compatible Storage
48
90
49
-## AI Review
91
+Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, Wasabi, Backblaze B2, and any S3-compatible service.
50
92
51
93
| Variable | Description |
52
94
|----------|-------------|
53
-| `AI_PROVIDER` | `openai` or `anthropic` |
54
-| `OPENAI_API_KEY` | OpenAI API Key |
55
-| `ANTHROPIC_API_KEY` | Anthropic API Key |
95
+| `STORAGE_BUCKET` | Bucket name (required for S3) |
96
+| `STORAGE_REGION` | S3 region (default: `us-east-1`) |
97
+| `STORAGE_ENDPOINT` | Custom S3 endpoint (for MinIO, R2, etc.) |
98
+| `STORAGE_ACCESS_KEY_ID` | S3 access key |
99
+| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key |
100
+
101
+:::tip[Performance]
102
+For best performance, use **local SSD storage**. Object storage (S3/R2) introduces latency. Use S3 for multi-node deployments or backup.
103
+:::
104
+
105
+## CI/CD Runner
106
+
107
+| Variable | Description | Default |
108
+|----------|-------------|---------|
109
+| `RUNNER_ENABLED` | Enable the CI runner | `true` |
110
+| `RUNNER_DOCKER_SOCKET` | Docker socket path | `/var/run/docker.sock` |
111
+| `RUNNER_WORKSPACE_PATH` | Workspace directory | `./data/runner/workspaces` |
112
+| `RUNNER_ARTIFACTS_PATH` | Artifacts directory | `./data/runner/artifacts` |
113
+| `RUNNER_CONCURRENT_JOBS` | Max concurrent jobs | `2` |
114
+| `RUNNER_TIMEOUT` | Job timeout in seconds | `3600` |
115
+| `RUNNER_REGISTRATION_TOKEN_TTL_DAYS` | Token TTL | `90` |
56
116
57
117
## Email (SMTP)
58
118
119
+| Variable | Description | Default |
120
+|----------|-------------|---------|
121
+| `SMTP_HOST` | SMTP hostname | — |
122
+| `SMTP_PORT` | SMTP port | `587` |
123
+| `SMTP_USER` | SMTP username | — |
124
+| `SMTP_PASSWORD` | SMTP password | — |
125
+| `SMTP_FROM` | From address | `[email protected]` |
126
+
127
+## Logging
128
+
129
+| Variable | Description | Default |
130
+|----------|-------------|---------|
131
+| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` | `info` |
132
+| `LOG_FILE` | Log file path | `./data/logs/opencodehub.log` |
133
+
134
+## Rate Limiting
135
+
136
+| Variable | Description | Default |
137
+|----------|-------------|---------|
138
+| `RATE_LIMIT_ENABLED` | Enable rate limiting | `true` |
139
+| `RATE_LIMIT_AUTH` | Auth endpoints (req/15min) | `5` |
140
+| `RATE_LIMIT_API` | API endpoints (req/min) | `100` |
141
+| `RATE_LIMIT_GIT` | Git operations (req/min) | `200` |
142
+| `RATE_LIMIT_GENERAL` | General endpoints (req/min) | `60` |
143
+| `RATE_LIMIT_SKIP_DEV` | Skip in development | `false` |
144
+| `CSRF_SKIP_DEV` | Skip CSRF in development | `false` |
145
+
146
+## CORS & Request Limits
147
+
148
+| Variable | Description | Default |
149
+|----------|-------------|---------|
150
+| `ALLOWED_ORIGINS` | Comma-separated allowed origins | `http://localhost:4321` |
151
+| `MAX_REQUEST_BODY_MB` | Max request body size (MB) | `10` |
152
+| `MAX_PACK_SIZE_MB` | Max git pack size (MB) | `500` |
153
+| `GIT_PROCESS_TIMEOUT_SECS` | Git process timeout (sec) | `300` |
154
+
155
+## Feature Flags
156
+
157
+| Variable | Description | Default |
158
+|----------|-------------|---------|
159
+| `ENABLE_REGISTRATION` | Allow new user signups | `true` |
160
+| `ENABLE_WIKI` | Enable repository wikis | `true` |
161
+| `ENABLE_ISSUES` | Enable issue tracking | `true` |
162
+| `ENABLE_ACTIONS` | Enable CI/CD pipelines | `true` |
163
+| `ENABLE_PACKAGES` | Enable package registry | `false` |
164
+| `ENABLE_AI_ASSISTANT` | Enable AI code review | `true` |
165
+
166
+## AI Providers
167
+
168
+Configure one or more AI providers for code review. Keys are encrypted at rest using `AI_CONFIG_ENCRYPTION_KEY`.
169
+
170
+| Variable | Provider |
171
+|----------|----------|
172
+| `OPENAI_API_KEY` | OpenAI (GPT-4, GPT-4o) |
173
+| `ANTHROPIC_API_KEY` | Anthropic (Claude) |
174
+| `GROQ_API_KEY` | Groq (fast inference) |
175
+| `BYTEZ_API_KEY` | Bytez |
176
+| `OPENROUTER_API_KEY` | OpenRouter (multi-model) |
177
+| `TOGETHER_API_KEY` | Together AI |
178
+| `GOOGLE_AI_API_KEY` | Google AI (Gemini) |
179
+
180
+Ollama is supported for local/self-hosted AI. No API key required — just set the Ollama endpoint in the admin dashboard.
181
+
182
+## External Review Agent
183
+
184
+| Variable | Description |
185
+|----------|-------------|
186
+| `EXTERNAL_AGENT_WEBHOOK_URL` | Webhook URL for external AI agent |
187
+| `EXTERNAL_AGENT_API_KEY` | API key for the external agent |
188
+| `EXTERNAL_AGENT_CALLBACK_SECRET` | Callback verification secret |
189
+
190
+## Backup
191
+
192
+| Variable | Description | Default |
193
+|----------|-------------|---------|
194
+| `BACKUP_ENCRYPTION_KEY` | Encryption key for backups | — |
195
+| `BACKUP_S3_BUCKET` | S3 bucket for backups | — |
196
+| `BACKUP_S3_PREFIX` | S3 prefix | `opencodehub-backups` |
197
+| `BACKUP_RETENTION_DAYS` | Retention period | `30` |
198
+| `BACKUP_DIR` | Local backup directory | `./backups` |
199
+
200
+## Air-gapped Mode
201
+
202
+| Variable | Description | Default |
203
+|----------|-------------|---------|
204
+| `AIR_GAPPED_MODE` | Disable all external API calls | `false` |
205
+
206
+When enabled, OpenCodeHub operates fully offline. AI features are disabled unless you run a local model (e.g., Ollama).
207
+
208
+## Observability
209
+
59
210
| Variable | Description |
60
211
|----------|-------------|
61
-| `SMTP_HOST` | Hostname (e.g. `smtp.gmail.com`) |
62
-| `SMTP_PORT` | Port (e.g. `587`) |
63
-| `SMTP_USER` | Username |
64
-| `SMTP_PASSWORD`| Password |
65
-| `SMTP_FROM` | From address |
212
+| `GRAFANA_OTLP_ENDPOINT` | Grafana Cloud OTLP endpoint |
213
+| `GRAFANA_INSTANCE_ID` | Grafana Cloud instance ID |
214
+| `GRAFANA_API_KEY` | Grafana Cloud API key |
215
+
216
+OpenCodeHub exports OpenTelemetry traces and Prometheus metrics. See [Monitoring](/administration/monitoring/) for setup details.
66
217
▼docs-site/src/content/docs/administration/deploy-nas.md
+232-35
3
3
description: "Deploy OpenCodeHub on your Network Attached Storage device using Docker"
4
4
---
5
5
6
+# Deploy OpenCodeHub on NAS (Synology, TrueNAS, QNAP)
7
+
6
8
> **Difficulty:** Intermediate | **Time:** 30-45 minutes | **Method:** Docker/Container Station
7
9
8
10
Deploy OpenCodeHub on your **Network Attached Storage (NAS)** device. This is perfect for small teams, personal use, or as a private Git server at home. Works on Synology DSM, TrueNAS SCALE, QNAP QTS, and any NAS supporting Docker.
9
11
12
+> **Recommended:** the repository ships `docker-compose.nas.yml`, which is
13
+> the production-safe version of every snippet below. It runs the web app,
14
+> the **SSH Git server**, and the **background worker** as separate
15
+> containers, requires secrets instead of defaulting them, and binds every
16
+> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) —
17
+> perfect for a dedicated hard disk or NAS share:
18
+>
19
+> ```bash
20
+> cp .env.example .env # then fill in ALL secrets
21
+> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d
22
+> ```
23
+>
24
+> The inline YAML in this guide is a minimal reference. Note that the **web
25
+> app container does not run SSH Git** — if you use a snippet as-is, port
26
+> 2222 will not serve SSH. Add the `ssh-git` service (or use
27
+> `docker-compose.nas.yml`) for SSH access.
28
+
10
29
---
11
30
12
31
## Prerequisites
13
32
14
33
| NAS Type | Minimum Requirements |
15
34
|----------|---------------------|
16
-| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 |
35
+| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 (better performance on x86_64) |
17
36
| **TrueNAS SCALE** | 8GB RAM, 2 CPU cores, 40GB free space |
18
37
| **QNAP** | QTS 5.0+, 4GB RAM, Container Station installed |
19
38
| **Generic** | Docker/Podman support, 4GB RAM, 2 CPU cores |
20
39
21
-- A domain with DDNS configured (e.g., `git.yourddns.net`) OR local network access only
22
-- Port forwarding on your router (if accessing externally):
40
+- A domain with DDNS (Dynamic DNS) configured (e.g., `git.yourddns.net`) OR local network access only
41
+- Port forwarding configured on your router (if accessing externally):
23
42
- `80` → NAS:80
24
43
- `443` → NAS:443
25
44
- `2222` → NAS:2222 (for SSH Git, optional)
138
157
--link opencodehub-postgres:postgres \
139
158
--link opencodehub-redis:redis \
140
159
-p 4321:4321 \
141
- -p 2222:2222 \
142
160
-e NODE_ENV=production \
143
161
-e DATABASE_URL=postgresql://opencodehub:[email protected]:5432/opencodehub \
144
162
-e REDIS_URL=redis://:[email protected]:6379 \
148
166
-e CRON_SECRET=$(openssl rand -hex 32) \
149
167
-e RUNNER_SECRET=$(openssl rand -hex 32) \
150
168
-e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \
169
+ -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \
151
170
-e SITE_URL=https://git.yourddns.net \
152
171
-e STORAGE_TYPE=local \
153
172
-e STORAGE_PATH=/data/storage \
154
- -e REPOS_PATH=/data/repos \
155
- -e SSH_HOST_KEY_PATH=/data/ssh/host_key \
173
+ -e GIT_REPOS_PATH=/data/repos \
174
+ -e GIT_SSH_HOST_KEY=/data/ssh/host_key \
156
175
-v /volume1/opencodehub/repos:/data/repos \
157
176
-v /volume1/opencodehub/storage:/data/storage \
158
177
-v /volume1/opencodehub/ssh:/data/ssh \
159
178
-v /volume1/opencodehub/cache:/data/cache \
160
- swadhinbiswas/opencodehub:latest
179
+ opencodehub/opencodehub:latest
161
180
```
162
181
163
182
> **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`).
183
+>
184
+> **SSH Git:** the app container serves the web UI only. For SSH access,
185
+> run a second container from the same image with `PROCESS_TYPE=ssh` and
186
+> expose port 2222 (this is exactly what `docker-compose.nas.yml` does).
164
187
165
188
#### Option B: Build from Source
166
189
178
201
restart: unless-stopped
179
202
ports:
180
203
- "4321:4321"
181
- - "2222:2222"
182
204
environment:
183
205
- NODE_ENV=production
206
+ - PROCESS_TYPE=app
184
207
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
185
208
- REDIS_URL=redis://:YOUR_PASS@redis:6379
186
209
- JWT_SECRET=YOUR_SECRET
189
212
- CRON_SECRET=YOUR_SECRET
190
213
- RUNNER_SECRET=YOUR_SECRET
191
214
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
215
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
192
216
- SITE_URL=https://git.yourddns.net
193
217
- STORAGE_TYPE=local
194
218
- STORAGE_PATH=/data/storage
219
+ - GIT_REPOS_PATH=/data/repos
220
+ - REPOS_PATH=/data/repos
221
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
222
+ - GIT_SSH_PORT=2222
223
+ volumes:
224
+ - /volume1/opencodehub/repos:/data/repos
225
+ - /volume1/opencodehub/storage:/data/storage
226
+ - /volume1/opencodehub/ssh:/data/ssh
227
+ - /volume1/opencodehub/cache:/data/cache
228
+ depends_on:
229
+ - postgres
230
+ - redis
231
+ networks:
232
+ - och-net
233
+
234
+ ssh-git:
235
+ build:
236
+ context: /volume1/docker/OpenCodeHub
237
+ dockerfile: Dockerfile
238
+ container_name: opencodehub-ssh
239
+ restart: unless-stopped
240
+ ports:
241
+ - "2222:2222"
242
+ environment:
243
+ - NODE_ENV=production
244
+ - PROCESS_TYPE=ssh
245
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
246
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
247
+ - JWT_SECRET=YOUR_SECRET
248
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
249
+ - GIT_REPOS_PATH=/data/repos
250
+ - REPOS_PATH=/data/repos
251
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
252
+ - GIT_SSH_PORT=2222
253
+ volumes:
254
+ - /volume1/opencodehub/repos:/data/repos
255
+ - /volume1/opencodehub/storage:/data/storage
256
+ - /volume1/opencodehub/ssh:/data/ssh
257
+ - /volume1/opencodehub/cache:/data/cache
258
+ depends_on:
259
+ - postgres
260
+ - redis
261
+ networks:
262
+ - och-net
263
+
264
+ worker:
265
+ build:
266
+ context: /volume1/docker/OpenCodeHub
267
+ dockerfile: Dockerfile
268
+ container_name: opencodehub-worker
269
+ restart: unless-stopped
270
+ environment:
271
+ - NODE_ENV=production
272
+ - PROCESS_TYPE=worker
273
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
274
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
275
+ - JWT_SECRET=YOUR_SECRET
276
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
277
+ - GIT_REPOS_PATH=/data/repos
195
278
- REPOS_PATH=/data/repos
196
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
279
+ - STORAGE_TYPE=local
280
+ - STORAGE_PATH=/data/storage
197
281
volumes:
198
282
- /volume1/opencodehub/repos:/data/repos
199
283
- /volume1/opencodehub/storage:/data/storage
304
388
305
389
services:
306
390
app:
307
- image: swadhinbiswas/opencodehub:latest
391
+ image: opencodehub/opencodehub:latest
308
392
container_name: opencodehub
309
393
restart: unless-stopped
310
394
ports:
311
395
- "4321:4321"
312
- - "2222:2222"
313
396
environment:
314
397
- NODE_ENV=production
398
+ - PROCESS_TYPE=app
315
399
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
316
400
- REDIS_URL=redis://:YOUR_PASS@redis:6379
317
401
- JWT_SECRET=YOUR_SECRET
320
404
- CRON_SECRET=YOUR_SECRET
321
405
- RUNNER_SECRET=YOUR_SECRET
322
406
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
407
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
323
408
- SITE_URL=https://git.yourdomain.com
324
409
- STORAGE_TYPE=local
325
410
- STORAGE_PATH=/data/storage
411
+ - GIT_REPOS_PATH=/data/repos
412
+ - REPOS_PATH=/data/repos
413
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
414
+ - GIT_SSH_PORT=2222
415
+ volumes:
416
+ - /mnt/YOURPOOL/opencodehub/repos:/data/repos
417
+ - /mnt/YOURPOOL/opencodehub/storage:/data/storage
418
+ - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh
419
+ - /mnt/YOURPOOL/opencodehub/cache:/data/cache
420
+ depends_on:
421
+ - postgres
422
+ - redis
423
+ networks:
424
+ - och-net
425
+
426
+ ssh-git:
427
+ image: opencodehub/opencodehub:latest
428
+ container_name: opencodehub-ssh
429
+ restart: unless-stopped
430
+ ports:
431
+ - "2222:2222"
432
+ environment:
433
+ - NODE_ENV=production
434
+ - PROCESS_TYPE=ssh
435
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
436
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
437
+ - JWT_SECRET=YOUR_SECRET
438
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
439
+ - GIT_REPOS_PATH=/data/repos
440
+ - REPOS_PATH=/data/repos
441
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
442
+ - GIT_SSH_PORT=2222
443
+ volumes:
444
+ - /mnt/YOURPOOL/opencodehub/repos:/data/repos
445
+ - /mnt/YOURPOOL/opencodehub/storage:/data/storage
446
+ - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh
447
+ - /mnt/YOURPOOL/opencodehub/cache:/data/cache
448
+ depends_on:
449
+ - postgres
450
+ - redis
451
+ networks:
452
+ - och-net
453
+
454
+ worker:
455
+ image: opencodehub/opencodehub:latest
456
+ container_name: opencodehub-worker
457
+ restart: unless-stopped
458
+ environment:
459
+ - NODE_ENV=production
460
+ - PROCESS_TYPE=worker
461
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
462
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
463
+ - JWT_SECRET=YOUR_SECRET
464
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
465
+ - GIT_REPOS_PATH=/data/repos
326
466
- REPOS_PATH=/data/repos
327
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
467
+ - STORAGE_TYPE=local
468
+ - STORAGE_PATH=/data/storage
328
469
volumes:
329
470
- /mnt/YOURPOOL/opencodehub/repos:/data/repos
330
471
- /mnt/YOURPOOL/opencodehub/storage:/data/storage
446
587
447
588
#### Create OpenCodeHub:
448
589
449
-1. Search and install `swadhinbiswas/opencodehub:latest`
590
+1. Search and install `opencodehub/opencodehub:latest`
450
591
2. Name: `opencodehub`
451
592
3. Network: `opencodehub-network`
452
593
4. Environment variables (same as Synology example)
485
626
486
627
## Part 4: Generic NAS / Any Docker Host
487
628
488
-If your NAS supports Docker (via Portainer, Podman, or CLI) and you have SSH access, the absolute easiest way to deploy is using the 1-click install script:
629
+If your NAS supports Docker (via Portainer, Podman, or CLI):
489
630
490
-```bash
491
-# Connect to your NAS via SSH, then run:
492
-curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash
493
-```
494
-
495
-### Using Docker Compose (Manual Setup)
496
-
497
-If you prefer to deploy manually via Portainer or `docker-compose.yml`:
631
+### Using Docker Compose:
498
632
499
633
```bash
500
-# Create unified data directory
501
-mkdir -p ~/opencodehub-data
634
+# Create directories
635
+mkdir -p ~/opencodehub/{repos,storage,ssh,cache,postgres,redis}
502
636
503
637
# Create docker-compose.yml
504
638
cat > ~/opencodehub/docker-compose.yml << 'EOF'
506
640
507
641
services:
508
642
app:
509
- image: swadhinbiswas/opencodehub:latest
643
+ image: opencodehub/opencodehub:latest
510
644
container_name: opencodehub
511
645
restart: unless-stopped
512
646
ports:
513
647
- "4321:4321"
514
- - "2222:2222"
515
648
environment:
516
649
- NODE_ENV=production
650
+ - PROCESS_TYPE=app
517
651
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
518
652
- REDIS_URL=redis://:YOUR_PASS@redis:6379
519
653
- JWT_SECRET=YOUR_SECRET
522
656
- CRON_SECRET=YOUR_SECRET
523
657
- RUNNER_SECRET=YOUR_SECRET
524
658
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
659
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
525
660
- SITE_URL=https://git.yourdomain.com
526
- - DATA_DIR=/data
661
+ - STORAGE_TYPE=local
662
+ - STORAGE_PATH=/data/storage
663
+ - GIT_REPOS_PATH=/data/repos
664
+ - REPOS_PATH=/data/repos
665
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
666
+ - GIT_SSH_PORT=2222
667
+ volumes:
668
+ - ./repos:/data/repos
669
+ - ./storage:/data/storage
670
+ - ./ssh:/data/ssh
671
+ - ./cache:/data/cache
672
+ depends_on:
673
+ - postgres
674
+ - redis
675
+ networks:
676
+ - och-net
677
+
678
+ ssh-git:
679
+ image: opencodehub/opencodehub:latest
680
+ container_name: opencodehub-ssh
681
+ restart: unless-stopped
682
+ ports:
683
+ - "2222:2222"
684
+ environment:
685
+ - NODE_ENV=production
686
+ - PROCESS_TYPE=ssh
687
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
688
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
689
+ - JWT_SECRET=YOUR_SECRET
690
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
691
+ - GIT_REPOS_PATH=/data/repos
692
+ - REPOS_PATH=/data/repos
693
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
694
+ - GIT_SSH_PORT=2222
695
+ volumes:
696
+ - ./repos:/data/repos
697
+ - ./storage:/data/storage
698
+ - ./ssh:/data/ssh
699
+ - ./cache:/data/cache
700
+ depends_on:
701
+ - postgres
702
+ - redis
703
+ networks:
704
+ - och-net
705
+
706
+ worker:
707
+ image: opencodehub/opencodehub:latest
708
+ container_name: opencodehub-worker
709
+ restart: unless-stopped
710
+ environment:
711
+ - NODE_ENV=production
712
+ - PROCESS_TYPE=worker
713
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
714
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
715
+ - JWT_SECRET=YOUR_SECRET
716
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
717
+ - GIT_REPOS_PATH=/data/repos
718
+ - REPOS_PATH=/data/repos
719
+ - STORAGE_TYPE=local
720
+ - STORAGE_PATH=/data/storage
527
721
volumes:
528
- - ~/opencodehub-data:/data
722
+ - ./repos:/data/repos
723
+ - ./storage:/data/storage
724
+ - ./ssh:/data/ssh
725
+ - ./cache:/data/cache
529
726
depends_on:
530
727
- postgres
531
728
- redis
541
738
- POSTGRES_PASSWORD=YOUR_PASS
542
739
- POSTGRES_DB=opencodehub
543
740
volumes:
544
- - ~/opencodehub-data/postgres:/var/lib/postgresql/data
741
+ - ./postgres:/var/lib/postgresql/data
545
742
networks:
546
743
- och-net
547
744
551
748
restart: unless-stopped
552
749
command: redis-server --appendonly yes --requirepass YOUR_PASS
553
750
volumes:
554
- - ~/opencodehub-data/redis:/data
751
+ - ./redis:/data
555
752
networks:
556
753
- och-net
557
754
715
912
716
913
### QNAP
717
914
- Use **QuTS hero** (ZFS) for better data integrity
718
-- Enable **snapshots** for the opencodehub dataset
915
+- Enable ** snapshots** for the opencodehub dataset
719
916
- **QVPN** can secure remote access without exposing ports
720
917
721
918
---
722
919
723
920
## Next Steps
724
921
725
-- [Configure Email (SMTP)](/administration/configuration/#email)
726
-- [Set up Offsite Storage (S3/R2)](/guides/storage-adapters/) — highly recommended for NAS
727
-- [Enable Branch Protection](/guides/branch-protection/)
728
-- [Set up your First Stack](/tutorials/your-first-stack/)
922
+- [Configure Email (SMTP)](../administration/configuration.md#email)
923
+- [Set up Offsite Storage (S3/R2)](../guides/storage-adapters.md) — highly recommended for NAS (protects against drive failure)
924
+- [Enable Branch Protection](../guides/branch-protection.md)
925
+- [Set up your First Stack](../tutorials/your-first-stack.md)
729
926
▼docs-site/src/content/docs/administration/deployment.md
+38-41
105
105
> removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT`
106
106
> for object storage. See `docs/guides/storage-adapters.md` for the
107
107
> full list of compatible object stores.
108
-```bash
109
-STORAGE_TYPE=gdrive
110
-GOOGLE_CLIENT_ID=...
111
-GOOGLE_CLIENT_SECRET=...
112
-GOOGLE_REFRESH_TOKEN=...
113
-GOOGLE_FOLDER_ID=...
114
-```
115
108
116
109
**Optional but Recommended:**
117
110
```bash
118
-# Error Monitoring
119
-SENTRY_DSN=https://your-sentry-dsn
120
-
121
111
# Email
122
112
SMTP_HOST=smtp.example.com
123
113
SMTP_PORT=587
126
116
127
117
128
118
# Redis (for session management)
129
-REDIS_URL=redis://localhost:6379
119
+REDIS_URL=redis://:your-password@localhost:6379
130
120
```
131
121
132
122
### Deployment Steps
147
137
bun run src/lib/env-validation.ts
148
138
# Should output: ✅ Environment validation passed
149
139
150
-# 5. Run database migrations
151
-npm run db:push
152
-# or generate versioned migrations:
153
-npm run db:generate
154
-npm run db:migrate
140
+# 5. Run database migrations (applies committed drizzle/ migrations)
141
+bun run migrate
142
+# or for dev: npm run db:push
155
143
156
144
# 6. Create admin user
157
145
bun run scripts/seed-admin.ts
160
148
# 7. Build for production
161
149
npm run build
162
150
163
-# 8. Start production server
164
-npm start
151
+# 8. Start the production server (built in step 7)
152
+npm start # → node ./dist/server/entry.mjs
165
153
```
166
154
155
+> **Docker** is the recommended path for production. Use the repository's
156
+> `docker-compose.yml` (quickstart) or `docker-compose.production.yml`
157
+> (Caddy TLS + isolated networks) — do not hand-roll a compose file.
158
+
167
159
### Docker Deployment (Recommended)
168
160
169
-```yaml
170
-# docker-compose.prod.yml
171
-version: '3.8'
161
+Use the repository's own compose files — they are kept in sync with the
162
+codebase and are production-safe (no default secrets, migrations applied
163
+at boot, worker + SSH services included):
164
+
165
+```bash
166
+# Quickstart (app + SSH + worker + PostgreSQL + Redis)
167
+cp .env.example .env # then set ALL secrets
168
+docker compose up -d
169
+
170
+# Production profile (adds Caddy TLS termination + isolated networks)
171
+docker compose -f docker-compose.production.yml --profile production up -d
172
+
173
+# NAS / dedicated-disk deployments
174
+OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d
175
+```
172
176
177
+Minimal reference compose (for illustration only):
178
+
179
+```yaml
173
180
services:
174
181
app:
175
182
build: .
176
183
restart: always
177
184
ports:
178
- - "3000:3000"
179
- env_file: .env.production
185
+ - "4321:4321"
186
+ env_file: .env
187
+ environment:
188
+ NODE_ENV: production
180
189
depends_on:
181
190
- postgres
182
191
- redis
183
- volumes:
184
- - ./data:/app/data
185
- environment:
186
- NODE_ENV: production
187
- healthcheck:
188
- test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
189
- interval: 30s
190
- timeout: 10s
191
- retries: 3
192
192
193
193
postgres:
194
- image: postgres:15-alpine
194
+ image: postgres:16-alpine
195
195
restart: always
196
196
environment:
197
197
POSTGRES_DB: opencodehub
198
198
POSTGRES_USER: opencodehub
199
199
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
200
- volumes:
201
- - postgres_data:/var/lib/postgresql/data
202
- ports:
203
- - "5432:5432"
204
200
205
201
redis:
206
202
image: redis:7-alpine
207
203
restart: always
208
204
command: redis-server --requirepass ${REDIS_PASSWORD}
209
- ports:
210
- - "6379:6379"
211
205
212
206
volumes:
213
207
postgres_data:
214
208
```
215
209
210
+> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git).
211
+> PostgreSQL (5432) and Redis (6379) stay on an internal network.
212
+
216
213
```bash
217
214
# Deploy with Docker
218
215
docker-compose -f docker-compose.prod.yml up -d
243
240
244
241
# Proxy configuration
245
242
location / {
246
- proxy_pass http://localhost:3000;
243
+ proxy_pass http://localhost:4321;
247
244
proxy_set_header Host $host;
248
245
proxy_set_header X-Real-IP $remote_addr;
249
246
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
257
254
258
255
# Git operations (larger timeouts)
259
256
location ~ ^/[^/]+/[^/]+\.git/ {
260
- proxy_pass http://localhost:3000;
257
+ proxy_pass http://localhost:4321;
261
258
proxy_read_timeout 600s;
262
259
proxy_send_timeout 600s;
263
260
proxy_buffer_size 128k;
264
261
▼docs-site/src/content/docs/administration/expose-nas.md
+9-9
42
42
43
43
Whatever exposure method you pick, you need:
44
44
45
-1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`).
45
+1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`).
46
46
2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel.
47
47
3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service.
48
48
4. **Trusted Proxies** correctly set in OpenCodeHub (so the real client IP is preserved and rate limits are not bypassed). See `TRUSTED_PROXIES` in `.env.example`.
104
104
# in the Tailscale admin console: https://login.tailscale.com/admin
105
105
# (turn on the "Funnel" feature flag).
106
106
#
107
-# Then on the NAS, expose the port OpenCodeHub listens on (default 3000):
108
-sudo tailscale funnel 3000 on
107
+# Then on the NAS, expose the port OpenCodeHub listens on (default 4321):
108
+sudo tailscale funnel 4321 on
109
109
110
110
# Or expose a specific path with HTTPS-only:
111
-sudo tailscale funnel --https=443 http://localhost:3000
111
+sudo tailscale funnel --https=443 http://localhost:4321
112
112
```
113
113
114
114
You will get a public URL like `https://nas-hostname.tail-net.ts.net`.
136
136
run:
137
137
138
138
```bash
139
-sudo tailscale funnel 3000 on
139
+sudo tailscale funnel 4321 on
140
140
```
141
141
142
142
For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command
266
266
|---|---|
267
267
| Subdomain | `git` (or anything) |
268
268
| Domain | `example.com` |
269
-| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) |
269
+| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) |
270
270
271
271
If you used Option A (Docker bridge network), `opencodehub` must be the
272
272
service name in your OpenCodeHub `docker-compose.yml`. If you used
273
-host networking, use `localhost:3000`.
273
+host networking, use `localhost:4321`.
274
274
275
275
Save. After a few seconds, `https://git.example.com` should reach your
276
276
OpenCodeHub instance.
382
382
- Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust
383
383
dashboard.
384
384
- Confirm OpenCodeHub is reachable from the tunnel's network namespace:
385
- - Tailscale (host networking): `curl http://localhost:3000`
386
- - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/`
385
+ - Tailscale (host networking): `curl http://localhost:4321`
386
+ - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/`
387
387
388
388
### Real client IP is wrong
389
389
390
390
▼docs-site/src/content/docs/administration/horizontal-scaling.md
+17-0
1
+# Horizontal Scaling
2
+
3
+OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads.
4
+
5
+## Stateless Application Servers
6
+The main Astro application and REST API endpoints are mostly stateless. You can run multiple instances of the app behind a load balancer (e.g., Nginx, HAProxy, or a cloud LB).
7
+
8
+## State Management
9
+- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS).
10
+- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers.
11
+- **Background Workers**: You can spin up multiple worker processes (`npm run worker:start`) across different nodes. They will pull jobs from the shared Redis queue.
12
+
13
+## Shared Git Storage
14
+To allow multiple app instances to access Git repositories over SSH and HTTP, the `GIT_REPOS_PATH` must point to a shared network file system (e.g., NFS, EFS) that supports POSIX file locking.
15
+
16
+## Pluggable Blob Storage
17
+Configure `STORAGE_TYPE=s3` and use an S3-compatible backend (MinIO, AWS S3, Cloudflare R2) so all nodes share access to the same artifact/LFS storage.
0
18
▼docs-site/src/content/docs/administration/monitoring.md
+2-0
12
12
13
13
- `GET /api/health` for liveness/readiness
14
14
- `GET /api/metrics` for Prometheus scraping
15
+ (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the
16
+ scrape config; see `deploy/prometheus.yml`)
15
17
- Grafana dashboards for request latency, queue health, and storage/DB timing
16
18
- Alerting for p95 regressions and service degradation
17
19
18
20
▼docs-site/src/content/docs/development/architecture.md
+105-54
3
3
description: "High-level overview of OpenCodeHub's components and data flow."
4
4
---
5
5
6
-import { Tabs, TabItem } from '@astrojs/starlight/components';
7
-
8
-OpenCodeHub is built for performance and scalability, using a modern stack optimized for self-hosting.
9
-
10
-## C4 Container Diagram
11
-
12
-This diagram visualizes the high-level interaction between the user, the CLI, and the server infrastructure.
13
-
14
-C4Container
15
- title Container diagram for OpenCodeHub System
16
-
17
- Person(developer, "Developer", "Uses CLI and Web UI to manage code")
18
-
19
- System_Boundary(c1, "OpenCodeHub Platform") {
20
- Container(cli, "CLI Tool", "TypeScript, Node.js", "Handles stacking, pushing, and rapid stack manipulation")
21
- Container(web_app, "Web Application", "Astro, SSR", "Serves UI and handles API requests")
22
- Container(git_server, "Git Server", "Node.js Stream", "Smart HTTP Protocol handler for cloning/pushing")
23
-
24
- ContainerDb(database, "PostgreSQL", "SQL Database", "Stores user data, PR metadata, and Merge Queue state")
25
- ContainerDb(redis, "Redis", "In-Memory", "Cache, Session Storage & Realtime Pub/Sub")
26
- ContainerDb(s3, "Object Storage", "S3 / MinIO", "Stores raw Git packfiles and LFS objects")
27
- }
28
-
29
- Rel(developer, cli, "Uses", "Terminal")
30
- Rel(developer, web_app, "Visits", "HTTPS")
31
-
32
- Rel(cli, web_app, "API Calls", "JSON/REST")
33
- Rel(cli, git_server, "Git Push/Pull", "HTTPS/Smart Protocol")
34
-
35
- Rel(web_app, database, "Reads/Writes", "SQL/Drizzle")
36
- Rel(web_app, redis, "Uses", "Cache & Sessions")
37
-
38
- Rel(git_server, s3, "Streams Objects", "S3 API")
39
- Rel(web_app, s3, "Reads Objects", "S3 API")
40
-
41
-## Component Breakdown
42
-
43
-<Tabs>
44
- <TabItem label="Web App (Astro)">
45
- The core of the system. Built with **Astro** in SSR mode.
46
- - **Frontend**: Lightweight, HTML-first pages with islands architecture.
47
- - **API**: Functional endpoints for the CLI and third-party tools.
48
- - **Auth**: Lucia Auth for secure session management.
49
- </TabItem>
50
- <TabItem label="Git Server">
51
- A custom implementation of the Git Smart HTTP protocol.
52
- - **Performance**: Streams packfiles directly to/from S3 without buffering to disk.
53
- - **Stateless**: Can be scaled horizontally across multiple nodes.
54
- </TabItem>
55
- <TabItem label="Storage Layer">
56
- - **Metadata**: PostgreSQL handles the complex relationships between PRs, comments, and repos.
57
- - **Git Data**: We treat Git objects as immutable blobs, perfect for S3-compatible storage (AWS, Cloudflare R2, MinIO).
58
- </TabItem>
59
-</Tabs>
6
+OpenCodeHub is a modular monolith built for performance and self-hosting simplicity.
7
+
8
+## System Overview
9
+
10
+```
11
+┌─────────────────────────────────────────────────────────────┐
12
+│ CLIENTS │
13
+│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │
14
+└─────────────────────────────────────────────────────────────┘
15
+ │
16
+┌─────────────────────────────────────────────────────────────┐
17
+│ OPENCODEHUB PLATFORM │
18
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
19
+│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │
20
+│ │ (Astro+React)│ │ (175+ routes)│ │ (src/pages/api/) │ │
21
+│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
22
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
23
+│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │
24
+│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │
25
+│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
26
+└─────────────────────────────────────────────────────────────┘
27
+ │
28
+┌─────────────────────────────────────────────────────────────┐
29
+│ PERSISTENCE & INFRASTRUCTURE │
30
+│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │
31
+└─────────────────────────────────────────────────────────────┘
32
+```
33
+
34
+## Runtime Processes
35
+
36
+| Process | Command | Purpose |
37
+|---------|---------|---------|
38
+| Main App | `npm run dev` | Web UI + API + Git HTTP |
39
+| SSH Git | `npm run git:start` | SSH git push/pull server |
40
+| Worker | `npm run worker:start` | Background jobs (BullMQ queues) |
41
+| Runner | `npm run runner:start` | CI/CD pipeline execution (Docker) |
42
+
43
+## Tech Stack
44
+
45
+| Layer | Technology |
46
+|-------|-----------|
47
+| Framework | Astro 4.x (SSR mode, `@astrojs/node` standalone adapter) |
48
+| UI | React 18 + Tailwind CSS + Radix UI primitives |
49
+| Database ORM | Drizzle ORM |
50
+| DB Drivers | PostgreSQL (pg), SQLite (better-sqlite3), LibSQL/Turso |
51
+| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) + SSO/SAML/OIDC |
52
+| Git | Native `git` CLI + `simple-git` + `isomorphic-git` + `nodegit` |
53
+| SSH | `ssh2` library |
54
+| CI/CD | Dockerode (Docker API) |
55
+| Storage | Abstract adapter: `local` filesystem or `s3`-compatible |
56
+| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez |
57
+| Queue | BullMQ + Redis |
58
+| Observability | OpenTelemetry + Prometheus + Pino |
59
+
60
+## Request Flow
61
+
62
+```
63
+Browser/Git CLI → Astro middleware (auth, rate limit, CSRF)
64
+ ↓
65
+ Route handler (src/pages/api/*.ts)
66
+ ↓
67
+ Business logic (src/lib/*.ts)
68
+ ↓
69
+ Database (src/db/index.ts → Drizzle)
70
+```
71
+
72
+## Authentication
73
+
74
+- **Web**: Cookie-based JWT (`och_session`)
75
+- **API**: Bearer token (JWT or Personal Access Token `och_...`)
76
+- **Git HTTP**: Basic auth with PAT as password
77
+- **Git SSH**: Public key auth against `deployKeys` table
78
+- **SSO**: SAML and OIDC via admin-configured providers
79
+
80
+## Git Protocol Handling
81
+
82
+- **HTTP**: `src/pages/git/` routes → `src/lib/git-server.ts` → spawns `git upload-pack/receive-pack`
83
+- **SSH**: `src/lib/ssh.ts` using `ssh2` library → spawns git processes
84
+- **Smart HTTP**: Full pkt-line protocol implementation with sideband
85
+
86
+## Storage Abstraction
87
+
88
+All blob storage goes through `StorageAdapter`:
89
+- `LocalStorageAdapter` — filesystem at `STORAGE_PATH`
90
+- `S3StorageAdapter` — any S3-compatible store (AWS, MinIO, R2, Garage, Wasabi, etc.)
91
+
92
+Path-style addressing is enabled automatically when `STORAGE_ENDPOINT` is set.
93
+
94
+## Database
95
+
96
+`src/db/index.ts` provides a factory pattern:
97
+- Reads `DATABASE_DRIVER` and `DATABASE_URL`
98
+- Returns appropriate Drizzle instance
99
+- **Production**: PostgreSQL required (schemas use `pgTable`)
100
+- **Dev**: SQLite or Turso for simplicity
101
+
102
+38 schema tables cover users, repos, PRs, issues, projects, workflows, merge queue, stacked PRs, AI reviews, security policies, and more.
103
+
104
+## Background Workers
105
+
106
+The worker process (`npm run worker:start`) handles:
107
+- BullMQ job queues for webhooks, notifications, CI triggers
108
+- Cron jobs (garbage collection, mirror sync, notification digests)
109
+- Email delivery via SMTP
110
+- Real-time SSE event broadcasting
60
111
▼docs-site/src/content/docs/development/contributing.md
+78-41
2
2
title: "Contributing Guide"
3
3
---
4
4
5
-
6
5
Thank you for your interest in contributing to OpenCodeHub! We welcome contributions from everyone.
7
6
8
-## 🛠 Local Development Setup
9
-
10
-To contribute code, you need to set up the project locally.
7
+## Local Development Setup
11
8
12
9
### Prerequisites
13
10
14
-- **Node.js**: v18 or v20 (LTS).
15
-- **Bun**: v1.0+ (Our package manager/runner).
16
-- **Docker**: For running the database locally.
11
+- **Node.js** 20+ (or Bun 1.0+)
12
+- **Docker** (for PostgreSQL and Redis)
13
+- **Git** 2.30+
17
14
18
15
### Step 1: Clone & Install
19
16
20
17
```bash
21
18
git clone https://github.com/swadhinbiswas/OpencodeHub.git
22
19
cd OpencodeHub
23
-
24
-# Install dependencies (fast!)
25
-bun install
20
+npm install
26
21
```
27
22
28
23
### Step 2: Database Setup
29
24
30
-Start a local Postgres and Redis instance using Docker:
25
+Start PostgreSQL and Redis:
31
26
32
27
```bash
33
28
docker-compose up -d postgres redis
34
29
```
35
30
36
-Copy the environment config:
31
+Copy and configure environment:
32
+
37
33
```bash
38
34
cp .env.example .env
35
+# The default .env.example works with docker-compose services
39
36
```
40
-*Note: The default `.env.example` is pre-configured to work with the docker-compose services.*
41
37
42
-Push the schema to the database:
38
+Push schema to database:
39
+
43
40
```bash
44
-bun db:push
41
+npm run db:push
45
42
```
46
43
47
-### Step 3: Run the App
48
-
49
-Start the development server:
44
+### Step 3: Seed & Run
50
45
51
46
```bash
52
-bun dev
47
+# Create admin user
48
+bun run scripts/seed-admin.ts
49
+
50
+# Start dev server
51
+npm run dev
53
52
```
54
53
55
-Visit `http://localhost:3000`.
54
+Visit `http://localhost:4321`.
56
55
56
+## Running Tests
57
57
58
-## 🧪 Running Tests
59
-
60
-We use **Vitest** for unit tests. Use TDD!
58
+We use **Vitest** for unit/integration tests and **Playwright** for E2E tests.
61
59
62
60
```bash
63
61
# Run all tests
64
-bun test
62
+npm run test
63
+
64
+# Run with coverage
65
+npm run test:coverage
65
66
66
67
# Run specific test file
67
-bun test user.test.ts
68
+npx vitest run tests/unit/auth.test.ts
68
69
69
70
# Watch mode
70
-bun test --watch
71
+npx vitest --watch
71
72
```
72
73
73
-
74
-## 🎨 Code Style
74
+**Current status: 546 tests passing across 114 test files (100% pass rate)**
75
75
76
-We use **ESLint** and **Prettier** to enforce code style.
76
+## Code Quality
77
77
78
-- **Lint**: `bun lint`
79
-- **Format**: `bun format`
78
+```bash
79
+npm run lint # Astro check
80
+npm run typecheck # TypeScript check
81
+npm run test # Test suite
82
+```
80
83
81
84
### Commit Messages
82
85
83
-We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
86
+We follow [Conventional Commits](https://www.conventionalcommits.org/):
84
87
85
88
- `feat: add new login page`
86
89
- `fix: resolve crash on startup`
87
90
- `docs: update troubleshooting guide`
91
+- `refactor: simplify permission check`
92
+- `test: add coverage for merge queue`
88
93
94
+## Submitting a Pull Request
95
+
96
+1. **Fork** the repository
97
+2. Create a **feature branch**: `git checkout -b feat/my-feature`
98
+3. Make your changes with tests
99
+4. Run `npm run test` and `npm run typecheck` to verify
100
+5. Commit with a conventional commit message
101
+6. Push to your fork
102
+7. Open a **Pull Request** against `main`
103
+8. Wait for CI checks to pass
104
+
105
+## Project Structure
106
+
107
+```
108
+src/
109
+├── pages/ # Astro file-based routing
110
+│ ├── api/ # REST API routes (175+ endpoints)
111
+│ └── [owner]/[repo]/ # Repository pages
112
+├── lib/ # Core business logic (130+ modules)
113
+├── db/ # Database layer (Drizzle ORM)
114
+│ ├── schema/ # 38 schema table definitions
115
+│ └── index.ts # DB connection factory
116
+├── components/ # React components
117
+├── runner/ # CI runner (Docker executor)
118
+└── middleware.ts # Auth, rate limit, CSRF
119
+
120
+cli/
121
+├── src/commands/ # CLI command groups (20+)
122
+└── src/lib/ # CLI utilities
123
+
124
+docs-site/ # This documentation site (Starlight)
125
+```
89
126
90
-## 🚀 Submitting a Pull Request
127
+## Areas for Contribution
91
128
92
-1. **Fork** the repository.
93
-2. Create a **feature branch**: `git checkout -b feat/my-feature`.
94
-3. Commit your changes.
95
-4. Push to your fork.
96
-5. Open a **Pull Request**.
97
-6. Wait for CI checks to pass.
98
-7. A team member will review your code!
129
+- **CI/CD Features**: Matrix builds, caching, workflow templates
130
+- **Package Registry**: npm and Docker registry enhancements
131
+- **AI Review**: New provider integrations, review quality improvements
132
+- **CLI**: New commands, better error messages
133
+- **Tests**: Increase coverage, especially for edge cases
134
+- **Documentation**: Tutorials, guides, API examples
135
+- **Accessibility**: WCAG compliance, keyboard navigation
99
136
100
-Thank you for helping build OpenCodeHub! ❤️
137
+Thank you for helping build OpenCodeHub!
101
138
▼docs-site/src/content/docs/development/database-schema.md
+65-7
2
2
title: "Database Schema"
3
3
---
4
4
5
+OpenCodeHub uses Drizzle ORM with 38 schema tables defined in `src/db/schema/`.
5
6
6
-The database schema is defined using Drizzle ORM in `drizzle/schema.ts`.
7
+## Schema Files
7
8
8
-## Core Tables
9
+| File | Tables | Purpose |
10
+|------|--------|---------|
11
+| `users.ts` | `users` | User accounts, passwords, OAuth, 2FA, admin flags |
12
+| `repositories.ts` | `repositories` | Git repos, visibility, disk paths, settings |
13
+| `pull-requests.ts` | `pullRequests` | PRs, states, branches, merge status |
14
+| `issues.ts` | `issues` | Issue tracking |
15
+| `projects.ts` | `projects` | Project boards, columns, cards |
16
+| `stacked-prs.ts` | `prStacks`, `prStackEntries` | Stacked PR relationships |
17
+| `merge-queue.ts` | `mergeQueue` | Queue entries with priority, CI status, attempts |
18
+| `workflows.ts` | `workflows`, `workflowRuns`, `workflowJobs`, `workflowSteps` | CI/CD pipeline execution |
19
+| `pipeline-runners.ts` | `pipelineRunners` | Registered CI runners |
20
+| `ai-reviews.ts` | `aiReviews` | AI code review results |
21
+| `ai-review-rules.ts` | `aiReviewRules` | AI review rule configurations |
22
+| `developer-metrics.ts` | `developerMetrics` | PR metrics, cycle time |
23
+| `branch-protection.ts` | `branchProtection`, `reviewRequirements`, `requiredStatusChecks` | Branch rules |
24
+| `webhooks.ts` | `webhooks` | Outbound webhook configs |
25
+| `automations.ts` | `automations` | Workflow automation rules |
26
+| `slack-integration.ts` | `slackIntegration` | Slack notification configs |
27
+| `organizations.ts` | `organizations` | Organizations |
28
+| `teams.ts` | `teams` | Team membership |
29
+| `roles.ts` | `roles` | Role-based access control |
30
+| `activity.ts` | `activity` | Activity/audit log |
31
+| `security.ts` | `security` | Security scan results |
32
+| `security-policies.ts` | `securityPolicies` | Security policy definitions |
33
+| `sso.ts` | `sso` | SSO/OIDC configuration |
34
+| `wiki.ts` | `wiki` | Repository wiki pages |
35
+| `deploy-keys.ts` | `deployKeys` | SSH deploy keys |
36
+| `path-permissions.ts` | `pathPermissions` | File-level access control |
37
+| `pr-states.ts` | `prStates` | Custom PR states |
38
+| `issue-statuses.ts` | `issueStatuses` | Custom issue statuses |
39
+| `custom-fields.ts` | `customFields` | Custom issue/project fields |
40
+| `auto-merge-rules.ts` | `autoMergeRules` | Auto-merge configuration |
41
+| `external-ci.ts` | `externalCIConfigs`, `externalBuilds` | External CI integration |
42
+| `review-rules.ts` | `reviewRules` | Review requirements and reviewer routing |
43
+| `integrations.ts` | `integrations` | Code quality, dashboard, deployment integrations |
44
+| `packages.ts` | `packages` | Package registry metadata |
45
+| `inbox-sections.ts` | `inboxSections` | Custom inbox sections |
46
+| `notification-preferences.ts` | `notificationPreferences` | Per-user notification settings |
47
+| `system-config.ts` | `systemConfig` | System-wide configuration |
9
48
10
-- **users**: Stores user accounts, hashed passwords, and profile info.
11
-- **repositories**: Stores repository metadata (owner, name, visibility).
12
-- **pull_requests**: Stores PR data, status, and description.
13
-- **sessions**: Stores active login sessions (if using database sessions).
49
+## Relationships
50
+
51
+Key relationships between tables:
52
+- `repositories` → `users` (owner)
53
+- `pullRequests` → `repositories` (repo) + `users` (author)
54
+- `prStacks` → `repositories` + `prStackEntries` → `pullRequests`
55
+- `mergeQueue` → `pullRequests` + `repositories`
56
+- `workflowRuns` → `workflows` + `repositories`
57
+- `issues` → `repositories` + `users`
58
+- `projects` → `repositories`
14
59
15
60
## Migrations
16
61
26
71
npm run db:migrate
27
72
```
28
73
29
-**Push schema (Prototyping):**
74
+**Push schema (prototyping — no migration files):**
30
75
```bash
31
76
npm run db:push
32
77
```
78
+
79
+**Open Drizzle Studio GUI:**
80
+```bash
81
+npm run db:studio
82
+```
83
+
84
+## Database Support
85
+
86
+| Driver | Use Case | Notes |
87
+|--------|----------|-------|
88
+| `postgres` | Production | Required. All schemas use `pgTable` |
89
+| `sqlite` | Development | Simple local setup |
90
+| `turso` | Edge (Vercel) | LibSQL for serverless deployments |
33
91
▼docs-site/src/content/docs/development/local-dev-setup.md
+73-19
5
5
6
6
## Prerequisites
7
7
8
-- Node.js 18+ (project `engines` specify Node 20+ for runtime)
9
-- Git 2.30+
10
-- PostgreSQL/MySQL/SQLite (PostgreSQL recommended)
8
+- **Node.js** 20+ (or Bun 1.0+)
9
+- **Git** 2.30+
10
+- **PostgreSQL** 14+ (recommended) or SQLite (for quick setup)
11
+- **Redis** 7+ (optional for local dev, required for background workers)
11
12
12
-## Install dependencies
13
+## Quick Setup
13
14
14
15
```bash
15
-npm install
16
-```
16
+# Clone the repository
17
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
18
+cd OpencodeHub
17
19
18
-## Environment
20
+# Install dependencies
21
+npm install
19
22
20
-```bash
23
+# Copy environment config
21
24
cp .env.example .env
22
-```
23
25
24
-Set required values like `JWT_SECRET`, `SESSION_SECRET`, `INTERNAL_HOOK_SECRET`, and `SITE_URL`.
26
+# Edit .env with your settings (at minimum, set the secrets)
27
+# Generate secrets with: openssl rand -base64 32
28
+```
25
29
26
30
## Database
27
31
28
32
```bash
33
+# Push schema to database (creates tables)
29
34
npm run db:push
35
+
36
+# Or generate and run migrations
37
+npm run db:generate
38
+npm run db:migrate
39
+
40
+# Open Drizzle Studio GUI
41
+npm run db:studio
42
+```
43
+
44
+For SQLite (simplest setup), set in `.env`:
45
+```
46
+DATABASE_DRIVER=sqlite
47
+DATABASE_URL=file:./data/opencodehub.db
30
48
```
31
49
32
-## Seed admin user
50
+## Seed Admin User
33
51
34
52
```bash
35
53
bun run scripts/seed-admin.ts
36
54
```
37
55
38
-## Run dev server
56
+## Start Development
39
57
40
58
```bash
59
+# Main app (Web UI + API + Git HTTP)
41
60
npm run dev
61
+
62
+# SSH Git server (optional, for ssh:// clone/push)
63
+npm run git:start
64
+
65
+# Background worker (optional, for webhooks/notifications)
66
+npm run worker:start
67
+
68
+# CI runner (optional, for pipeline execution)
69
+npm run runner:start
70
+```
71
+
72
+The dev server runs at `http://localhost:4321`.
73
+
74
+## Useful Scripts
75
+
76
+| Command | Purpose |
77
+|---------|---------|
78
+| `npm run dev` | Start Astro dev server |
79
+| `npm run build` | Production build |
80
+| `npm run test` | Run Vitest test suite |
81
+| `npm run test:coverage` | Run tests with coverage |
82
+| `npm run lint` | Run Astro check |
83
+| `npm run typecheck` | TypeScript type checking |
84
+| `npm run db:push` | Push schema to database |
85
+| `npm run db:generate` | Generate Drizzle migrations |
86
+| `npm run db:migrate` | Run migrations |
87
+| `npm run db:studio` | Open Drizzle Studio |
88
+| `npm run git:start` | Start SSH git server |
89
+| `npm run worker:start` | Start background worker |
90
+| `npm run runner:start` | Start CI runner |
91
+
92
+## Docker Compose (Alternative)
93
+
94
+For a full stack with PostgreSQL and Redis:
95
+
96
+```bash
97
+docker-compose up -d
98
+docker-compose exec app bun run scripts/seed-admin.ts
42
99
```
43
100
44
-Open the app at `http://localhost:3000`.
101
+This starts the app on port 4321 and SSH on port 2222.
45
102
46
-## Useful scripts
103
+## IDE Setup
47
104
48
-- `npm run db:generate`
49
-- `npm run db:migrate`
50
-- `npm run db:studio`
51
-- `npm run lint`
52
-- `npm run test`
105
+- **VS Code**: Install the Astro extension for syntax highlighting
106
+- **Recommended extensions**: Tailwind CSS IntelliSense, ESLint
53
107
▼docs-site/src/content/docs/development/release-gates.md
+21-0
1
+# Release Gates
2
+
3
+This document outlines the required gates that must be passed before any code can be merged into the default branch.
4
+
5
+## 1. Continuous Integration (CI)
6
+All pull requests must pass the following CI checks:
7
+- **Linting**: `npm run lint` must exit with 0.
8
+- **Typechecking**: `npm run typecheck` must exit with 0.
9
+- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass.
10
+
11
+## 2. Security Scans
12
+- **Secret Scanning**: Commits must be scanned for leaked secrets.
13
+- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold.
14
+- **Authorization Regression**: Core RBAC and permissions tests must pass.
15
+
16
+## 3. Documentation Parity
17
+Any feature addition must include updates to both documentation stacks:
18
+- The Markdown docs in `docs/`
19
+- The Starlight site in `docs-site/src/content/docs/`
20
+
21
+Features will not be merged if their documentation is missing or incomplete.
0
22
▼docs-site/src/content/docs/features/ai-review.md
+88-459
2
2
title: "AI Code Review"
3
3
---
4
4
5
-> Get instant, intelligent code reviews powered by GPT-4 or Claude
6
-
7
5
AI Code Review uses advanced language models to automatically review your pull requests, catching bugs, security issues, and code quality problems before they reach production.
8
6
9
-## 📖 Table of Contents
10
-
11
-- [Overview](#overview)
12
-- [Setup](#setup)
13
- - [Configure AI Provider](#configure-ai-provider)
14
- - [Enable for Repository](#enable-for-repository)
15
-- [Using AI Review](#using-ai-review)
16
-- [Understanding Review Output](#understanding-review-output)
17
-- [Customizing Reviews](#customizing-reviews)
18
-- [Cost Considerations](#cost-considerations)
19
-- [Best Practices](#best-practices)
20
-- [Troubleshooting](#troubleshooting)
7
+## Supported Providers
21
8
9
+OpenCodeHub supports 10+ AI providers. Configure one or more in your `.env` file:
22
10
23
-## Overview
11
+| Provider | Models | API Key Env Var |
12
+|----------|--------|-----------------|
13
+| **OpenAI** | GPT-4, GPT-4o, GPT-4 Turbo | `OPENAI_API_KEY` |
14
+| **Anthropic** | Claude 3.5 Sonnet, Claude 3 Opus | `ANTHROPIC_API_KEY` |
15
+| **Google** | Gemini 1.5 Pro, Gemini Flash | `GOOGLE_AI_API_KEY` |
16
+| **Groq** | Llama 3, Mixtral (fast inference) | `GROQ_API_KEY` |
17
+| **OpenRouter** | Multi-model gateway | `OPENROUTER_API_KEY` |
18
+| **Together** | Llama, Mixtral, CodeLlama | `TOGETHER_API_KEY` |
19
+| **Bytez** | Various models | `BYTEZ_API_KEY` |
20
+| **Ollama** | Any local model (Llama, CodeLlama, etc.) | No key needed |
21
+| **External Agent** | Custom webhook | `EXTERNAL_AGENT_WEBHOOK_URL` |
24
22
25
-### What AI Review Catches
23
+:::tip[Recommended]
24
+For best results, use **GPT-4o** or **Claude 3.5 Sonnet**. For fast/cheap reviews, use **Groq** or **Ollama** with a local model.
25
+:::
26
26
27
-🔒 **Security Vulnerabilities**
27
+## What AI Review Catches
28
28
29
-- SQL injection risks
30
-- XSS (Cross-Site Scripting) vectors
31
-- Authentication bypasses
32
-- Secrets/credentials in code
29
+**Security Vulnerabilities**
30
+- SQL injection, XSS, CSRF
31
+- Authentication/authorization bypasses
32
+- Exposed secrets or credentials
33
33
- Insecure dependencies
34
34
35
-⚡ **Performance Issues**
36
-
35
+**Performance Issues**
37
36
- N+1 database queries
38
-- Inefficient algorithms (O(n²) → O(n log n))
37
+- Inefficient algorithms
39
38
- Memory leaks
40
-- Blocking operations in async code
41
39
- Missing indexes
42
40
43
-📝 **Code Quality**
44
-
41
+**Code Quality**
45
42
- Best practice violations
46
43
- Code smells (long functions, deep nesting)
47
44
- Incomplete error handling
48
45
- Missing input validation
49
-- Poor naming conventions
50
-
51
-🐛 **Potential Bugs**
52
46
47
+**Potential Bugs**
53
48
- Null/undefined pointer exceptions
54
49
- Race conditions
55
50
- Off-by-one errors
56
-- Logic flaws
57
51
- Type mismatches
58
52
59
-### How It Works
53
+## How It Works
60
54
61
55
```
62
56
1. Developer opens/updates PR
63
57
↓
64
-2. OpenCodeHub sends diff to AI
58
+2. OpenCodeHub sends diff to AI provider
65
59
↓
66
60
3. AI analyzes code changes
67
61
↓
71
64
5. Developer addresses feedback
72
65
```
73
66
74
-**Privacy note:** Code is sent to AI provider (OpenAI/Anthropic). Use on-premise models for sensitive code.
75
-
67
+**Privacy note:** Code is sent to the configured AI provider. Use Ollama or the external agent for sensitive codebases.
76
68
77
69
## Setup
78
70
79
-### Configure AI Provider
80
-
81
-**Option 1: OpenAI (GPT-4)**
82
-
83
-1. Get API key from [platform.openai.com](https://platform.openai.com/api-keys)
84
-
85
-2. Add to `.env`:
71
+### Step 1: Configure AI Provider
86
72
73
+**OpenAI:**
87
74
```bash
88
- AI_PROVIDER=openai
89
- OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
90
- OPENAI_MODEL=gpt-4-turbo-preview # or gpt-4, gpt-3.5-turbo
75
+OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
91
76
```
92
77
93
-3. Restart OpenCodeHub
94
-
95
-**Option 2: Anthropic (Claude)**
96
-
97
-1. Get API key from [console.anthropic.com](https://console.anthropic.com/)
98
-
99
-2. Add to `.env`:
100
-
78
+**Anthropic:**
101
79
```bash
102
- AI_PROVIDER=anthropic
103
- ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx
104
- ANTHROPIC_MODEL=claude-3-opus-20240229 # or claude-3-sonnet
80
+ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx
105
81
```
106
82
107
-3. Restart OpenCodeHub
108
-
109
-**Option 3: Azure OpenAI**
110
-
83
+**Google Gemini:**
111
84
```bash
112
-AI_PROVIDER=azure-openai
113
-AZURE_OPENAI_API_KEY=xxxxx
114
-AZURE_OPENAI_ENDPOINT=https://yourinstance.openai.azure.com/
115
-AZURE_OPENAI_DEPLOYMENT=your-deployment-name
85
+GOOGLE_AI_API_KEY=xxxxxxxxxxxxx
116
86
```
117
87
118
-**Option 4: Self-Hosted (Coming Soon)**
119
-
120
-OpenCodeHub will support local models via Ollama:
88
+**Groq (fast, cheap):**
89
+```bash
90
+GROQ_API_KEY=gsk_xxxxxxxxxxxxx
91
+```
121
92
93
+**Ollama (local, private):**
122
94
```bash
123
-AI_PROVIDER=ollama
124
-OLLAMA_ENDPOINT=http://localhost:11434
125
-OLLAMA_MODEL=codellama:13b
95
+# No env var needed — configure in Settings → AI
96
+# Just install Ollama: https://ollama.com
97
+# Pull a model: ollama pull codellama:13b
126
98
```
127
99
128
-### Enable for Repository
100
+**Multiple providers:** Configure all keys. The system picks the best available provider.
129
101
130
-**Via Web UI:**
102
+### Step 2: Enable for Repository
131
103
132
-1. Navigate to **Repository → Settings → AI Review**
133
-2. Toggle **"Enable AI Code Review"**
134
-3. Configure settings:
135
- - **Trigger**: `On PR open`, `On push`, or `Manual only`
136
- - **Auto-comment**: Post comments automatically
137
- - **Severity threshold**: `All`, `Warning+`, or `Critical only`
138
- - **Max files**: Limit review to N files (cost control)
104
+**Via Web UI:**
105
+1. Go to **Repository → Settings → AI Review**
106
+2. Toggle **Enable AI Code Review**
107
+3. Configure trigger, severity threshold, and max files
139
108
4. Click **Save**
140
109
141
110
**Via API:**
142
-
143
111
```bash
144
-curl -X PATCH https://git.yourcompany.com/api/repos/123/ai-review \
112
+curl -X PATCH https://git.yourcompany.com/api/repos/OWNER/REPO/settings \
145
113
-H "Authorization: Bearer $TOKEN" \
146
- -d '{
147
- "enabled": true,
148
- "trigger": "on_pr_open",
149
- "auto_comment": true,
150
- "severity_threshold": "warning"
151
- }'
114
+ -d '{ "aiReview": { "enabled": true } }'
152
115
```
153
116
154
117
**Via CLI:**
155
-
156
118
```bash
157
-och repo config ai-review --enable \
158
- --trigger on_pr_open \
159
- --auto-comment
119
+och repo config ai-review --enable
160
120
```
161
121
162
-
163
122
## Using AI Review
164
123
165
124
### Automatic Reviews
166
125
167
-If enabled, AI reviews trigger automatically:
168
-
169
-```bash
170
-# 1. Create PR
171
-git push origin feature-branch
172
-# → AI review starts automatically
173
-
174
-# 2. Update PR
175
-git push origin feature-branch
176
-# → AI re-reviews changed files only
177
-```
178
-
179
-You'll see:
180
-
181
-- ✅ **Status check**: "AI Review: In Progress" → "AI Review: Complete"
182
-- 💬 **Inline comments**: On specific lines with issues
183
-- 📊 **Summary comment**: Overall assessment
126
+When enabled, AI reviews trigger automatically when PRs are opened or updated.
184
127
185
128
### Manual Reviews
186
129
187
-Request AI review on-demand:
188
-
189
-**Via Web UI:**
190
-
191
-1. Open PR page
192
-2. Click **"Request AI Review"** button
193
-3. Wait ~30-60 seconds
194
-4. Review appears as comments
195
-
196
-**Via CLI:**
130
+**Web UI:** Open PR page → Click "Request AI Review"
197
131
132
+**CLI:**
198
133
```bash
199
-# Review specific PR
200
-och review ai 125
201
-
202
-# Wait for the review to complete
203
-och review ai 125 --wait
134
+och review ai <pr-number>
204
135
```
205
136
206
-**Via API:**
207
-
137
+**API:**
208
138
```bash
209
139
curl -X POST https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/125/ai-review \
210
140
-H "Authorization: Bearer $TOKEN"
211
141
```
212
142
213
-
214
143
## Understanding Review Output
215
144
216
145
### Severity Levels
217
146
218
-AI assigns severity to each finding:
219
-
220
-| Severity | Icon | Meaning | Example |
221
-| --------------- | ---------- | -------------------------------------- | ----------------------------------------- |
222
-| 🔴 **CRITICAL** | Must fix | Security vulnerability, data loss risk | SQL injection, exposed API key |
223
-| 🟡 **WARNING** | Should fix | Performance issue, bad practice | N+1 query, missing error handling |
224
-| 🔵 **INFO** | Consider | Suggestion, refactoring opportunity | Better variable name, code simplification |
225
-
226
-### Example Review Comment
227
-
228
-````markdown
229
-🔴 CRITICAL: SQL Injection Vulnerability
230
-
231
-**File:** `src/api/users.ts`
232
-**Line:** 45
233
-
234
-**Issue:**
235
-User input is directly concatenated into SQL query without sanitization.
236
-
237
-**Current Code:**
238
-
239
-```typescript
240
-const query = `SELECT * FROM users WHERE email = '${userEmail}'`;
241
-```
242
-````
243
-
244
-**Risk:**
245
-An attacker could inject malicious SQL:
246
-
247
-```
248
-userEmail = "' OR '1'='1"
249
-→ Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1'
250
-→ Returns ALL users (authentication bypass)
251
-```
252
-
253
-**Fix:**
254
-Use parameterized queries or ORM:
255
-
256
-```typescript
257
-const query = `SELECT * FROM users WHERE email = ?`;
258
-const results = await db.query(query, [userEmail]);
259
-
260
-// Or with ORM:
261
-const user = await db.users.findOne({ email: userEmail });
262
-```
263
-
264
-**References:**
265
-
266
-- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection)
267
-- [Node.js SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)
268
-
269
-````
147
+| Severity | Meaning | Example |
148
+|----------|---------|---------|
149
+| **CRITICAL** | Must fix — security/data loss | SQL injection, exposed key |
150
+| **WARNING** | Should fix — perf/best practice | N+1 query, missing error handling |
151
+| **INFO** | Consider — suggestion | Better variable name |
270
152
271
153
### Summary Comment
272
154
273
-AI posts an overall summary:
274
-
275
-```markdown
276
-## 🤖 AI Code Review Summary
155
+AI posts an overall summary with statistics, critical issues, and recommendations.
277
156
278
-**Overall Assessment:** ⚠️ Needs Attention
157
+### Custom Rules
279
158
280
-### Statistics
281
-- Files reviewed: 8
282
-- Critical issues: 2
283
-- Warnings: 5
284
-- Info: 3
285
-
286
-### Critical Issues
287
-1. 🔴 SQL Injection in `src/api/users.ts:45`
288
-2. 🔴 Hardcoded API key in `src/config.ts:12`
289
-
290
-### Recommendations
291
-1. Add input validation to all API endpoints
292
-2. Move secrets to environment variables
293
-3. Consider adding integration tests for auth flows
294
-
295
-### Positive Notes
296
-- ✅ Good error handling in payment service
297
-- ✅ Well-structured database migrations
298
-- ✅ Comprehensive TypeScript types
299
-
300
-**Estimated fix time:** 2-3 hours
301
-````
302
-
303
-
304
-## Customizing Reviews
305
-
306
-### Review Rules
307
-
308
-Create `.opencodehub/ai-review.yml` in your repository:
159
+Create `.opencodehub/ai-review.yml` in your repo:
309
160
310
161
```yaml
311
-# AI Review Configuration
312
-
313
-# Which files to review
314
162
include:
315
163
- "src/**/*.ts"
316
- - "src/**/*.tsx"
317
- - "lib/**/*.js"
318
-
319
-# Which files to skip
320
164
exclude:
321
165
- "**/*.test.ts"
322
- - "**/*.spec.ts"
323
- - "**/generated/**"
324
166
- "dist/**"
325
167
326
-# Focus areas (weighted 1-10)
327
168
focus:
328
- security: 10 # Highest priority
169
+ security: 10
329
170
performance: 8
330
171
bugs: 8
331
172
code_quality: 5
332
- documentation: 3
333
-
334
-# Severity thresholds
335
-report:
336
- critical: always
337
- warning: always
338
- info: on_request # Only if --verbose flag used
339
173
340
-# Custom rules
341
174
custom_rules:
342
175
- name: "No console.log in production"
343
- pattern: 'console\\.log\\('
176
+ pattern: 'console\.log\('
344
177
severity: warning
345
178
message: "Remove console.log before merging"
346
-
347
- - name: "Require error handling"
348
- pattern: 'await.*\\(.*\\)'
349
- check: must_have_try_catch
350
- severity: warning
351
-
352
-# AI instructions
353
-instructions: >
354
- Pay special attention to:
355
- 1. Authentication and authorization logic
356
- 2. Database query efficiency
357
- 3. Input validation
358
- 4. Proper error handling
359
-
360
- Our coding standards:
361
- - TypeScript strict mode
362
- - Functional programming preferred
363
- - Maximum function length: 50 lines
364
- - Maximum cyclomatic complexity: 10
365
179
```
366
180
367
-### Custom Prompts
368
-
369
-Use **Settings → AI Review Rules** to add repository‑specific prompts and checks. Rules are applied automatically when AI reviews run.
370
-
371
-### Language-Specific Reviews
372
-
373
-AI automatically adapts to programming language:
374
-
375
-- **TypeScript/JavaScript**: Type safety, async/await, React best practices
376
-- **Python**: PEP 8, type hints, Django/Flask patterns
377
-- **Go**: Go idioms, error handling, concurrency
378
-- **Rust**: Borrow checker, unsafe code, idiomatic Rust
379
-- **Java**: Design patterns, Spring best practices, thread safety
380
-
181
+Or use **Settings → AI Review Rules** in the web UI.
381
182
382
183
## Cost Considerations
383
184
384
-### Pricing
385
-
386
-AI review costs depend on provider and model:
387
-
388
-**OpenAI (GPT-4 Turbo):**
389
-
390
-- ~$0.01 per 1K tokens (input)
391
-- ~$0.03 per 1K tokens (output)
392
-- **Typical PR**: 500-2000 tokens = **$0.02-0.10 per review**
393
-
394
-**Anthropic (Claude 3 Opus):**
395
-
396
-- ~$0.015 per 1K tokens (input)
397
-- ~$0.075 per 1K tokens (output)
398
-- **Typical PR**: **$0.03-0.15 per review**
399
-
400
-**Example monthly cost:**
401
-
402
-- 50 PRs/month × $0.05/review = **$2.50/month**
403
-- 500 PRs/month × $0.05/review = **$25/month**
404
-
405
-### Cost Optimization
406
-
407
-To reduce cost:
408
-
409
-- Keep PRs small and focused.
410
-- Avoid very large diffs in a single review.
185
+| Provider | Approx Cost per Review |
186
+|----------|----------------------|
187
+| Groq | Free (rate limited) |
188
+| Ollama | Free (local) |
189
+| OpenAI GPT-4o | ~$0.02-0.10 |
190
+| Anthropic Claude | ~$0.03-0.15 |
191
+| Google Gemini | ~$0.01-0.05 |
411
192
412
-**3. Skip test files:**
413
-
414
-```yaml
415
-exclude:
416
- - "**/*.test.*"
417
- - "**/*.spec.*"
418
-```
419
-
420
-**4. Review only on-request:**
421
-
422
-```yaml
423
-trigger: manual # Don't auto-review every push
424
-```
425
-
426
-**5. Use severity threshold:**
427
-
428
-```yaml
429
-severity_threshold: warning # Skip "info" level comments
430
-```
431
-
432
-
433
-## Best Practices
434
-
435
-### 💡 When to Use AI Review
436
-
437
-**✅ Good Use Cases:**
438
-
439
-- Security-sensitive code (auth, payments)
440
-- Complex algorithms
441
-- Database queries
442
-- API endpoints
443
-- Third-party integrations
444
-
445
-**❌ Less Useful:**
446
-
447
-- Generated code
448
-- Configuration files
449
-- Simple refactoring
450
-- Documentation-only changes
451
-
452
-### 💡 Responding to AI Feedback
453
-
454
-**1. Don't auto-fix everything**
455
-
456
-- AI can be wrong
457
-- Context matters
458
-- Use judgment
459
-
460
-**2. Engage in discussion**
461
-
462
-```markdown
463
-@ai-review That's a good catch, but in this case we're using a
464
-trusted source and the data is already sanitized earlier in the pipeline.
465
-
466
-See line 23 where we validate input.
467
-```
468
-
469
-**3. Track false positives**
470
-
471
-```yaml
472
-# .opencodehub/ai-review.yml
473
-ignore:
474
- - file: src/utils/legacy.ts
475
- reason: "Legacy code, requires major refactor"
476
- until: 2024-12-31
477
-```
478
-
479
-### 💡 Combining with Human Review
480
-
481
-AI complements, doesn't replace humans:
482
-
483
-```
484
-1. AI reviews first (catches obvious issues)
485
-2. Developer fixes critical/warnings
486
-3. Human reviewer approves
487
-```
488
-
489
-This workflow:
490
-
491
-- Reduces human reviewer burden
492
-- Catches more bugs
493
-- Faster feedback cycles
494
-
193
+**Cost optimization:**
194
+- Keep PRs small and focused
195
+- Skip test files in review config
196
+- Use severity threshold to filter info-level comments
197
+- Use Groq or Ollama for routine reviews, GPT-4/Claude for critical code
495
198
496
199
## Troubleshooting
497
200
498
-### "AI review taking too long"
499
-
500
-**Cause:** Large PR (many files/lines).
501
-
502
-**Solution:**
503
-
504
-```bash
505
-# Check review status
506
-och review status 125
507
-
508
-# Retry the review
509
-och review ai 125
510
-```
511
-
512
-### "AI review failed"
513
-
514
-**Causes:**
515
-
516
-1. API key invalid/expired
517
-2. Rate limit exceeded
518
-3. Timeout (very large PR)
519
-
520
-**Solutions:**
201
+**AI review taking too long:** Large PR (many files). Split into smaller PRs.
521
202
522
-```bash
523
-# 1. Verify API key
524
-echo $OPENAI_API_KEY
525
-
526
-# 2. Check rate limits
527
-curl https://api.openai.com/v1/usage \
528
- -H "Authorization: Bearer $OPENAI_API_KEY"
529
-
530
-# 3. Split large PR or review manually.
531
-```
532
-
533
-### "Too many false positives"
534
-
535
-**Solution:** Use **Settings → AI Review Rules** to refine prompts and reduce false positives.
536
-
537
-### "Missing obvious bugs"
538
-
539
-**Cause:** AI missed context or edge case.
540
-
541
-**Solution:** Add custom instructions in **Settings → AI Review Rules** so reviews incorporate your conventions.
542
-
543
-
544
-## Advanced: CI/CD Integration
545
-
546
-### Block merge on critical findings
203
+**AI review failed:** Check API key is valid. Verify provider is configured in Settings → AI.
547
204
548
-```yaml
549
-# .github/workflows/ai-review.yml
550
-name: AI Code Review
551
-on: [pull_request]
552
-
553
-jobs:
554
- ai-review:
555
- runs-on: ubuntu-latest
556
- steps:
557
- - name: Run AI Review
558
- run: och review ai ${{ github.event.pull_request.number }}
559
- env:
560
- OCH_TOKEN: ${{ secrets.OCH_TOKEN }}
561
-
562
- - name: Check for critical issues
563
- run: |
564
- REVIEW=$(curl -s \
565
- -H "Authorization: Bearer $OCH_TOKEN" \
566
- https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/${{ github.event.pull_request.number }}/ai-review/latest)
567
- SEVERITY=$(echo "$REVIEW" | jq -r '.data.overallSeverity')
568
- if [ "$SEVERITY" = "critical" ]; then
569
- echo "❌ Critical issues found"
570
- exit 1
571
- fi
572
-```
205
+**Too many false positives:** Refine rules in Settings → AI Review Rules. Add custom instructions.
573
206
207
+**Missing obvious bugs:** Add custom instructions describing your conventions and patterns.
574
208
575
209
## See Also
576
210
577
-- [Stacked Pull Requests](stacked-prs.md)
578
-- [Smart Merge Queue](merge-queue.md)
579
-- [CI/CD Actions](ci-actions.md)
580
-
581
-
582
-## Need Help?
583
-
584
-- 💬 [Discord Community](https://discord.gg/opencodehub)
585
-- 🐛 [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
586
-- 📖 [Full Documentation](/)
211
+- [Stacked PRs](/features/stacked-prs/)
212
+- [Merge Queue](/features/merge-queue/)
213
+- [CI/CD Actions](/features/ci-actions/)
214
+- [Configuration Reference](/administration/configuration/#ai-providers)
587
215
▼docs-site/src/content/docs/getting-started/first-repository.md
+73-32
2
2
title: "Create Your First Repository"
3
3
---
4
4
5
-
6
5
This guide walks you through the complete lifecycle of a repository in OpenCodeHub.
7
6
8
-## Step 1: Creation
9
-
10
-1. Click the **+** button in the top navbar and select **New Repository**.
11
-2. **Owner**: Select yourself or an organization.
12
-3. **Repository Name**: Enter `hello-world`.
13
-4. **Description**: "My first project on OpenCodeHub".
14
-5. **Visibility**: Select **Public**.
15
-6. **Initialize**: Check **Initialize with README**.
7
+## Step 1: Create the Repository
16
8
17
-Click **Create Repository**. You'll be taken to your new repository's home page.
9
+1. Click the **+** button in the top navbar → **New Repository**
10
+2. **Owner**: Select yourself or an organization
11
+3. **Repository Name**: Enter `hello-world`
12
+4. **Description**: "My first project on OpenCodeHub"
13
+5. **Visibility**: Select **Public**
14
+6. **Initialize**: Check **Initialize with README**
18
15
19
-## Step 2: Cloning
16
+Click **Create Repository**.
20
17
21
-To work on your computer, you need to clone the repository.
18
+## Step 2: Clone
22
19
23
20
### SSH (Recommended)
24
-Ensure you have added your SSH key in **Settings > SSH Keys**.
21
+
22
+Add your SSH key in **Settings → SSH Keys** first.
25
23
26
24
```bash
27
-git clone [email protected]:username/hello-world.git
25
+git clone ssh://git@your-server:2222/username/hello-world.git
28
26
```
29
27
30
28
### HTTPS
29
+
31
30
```bash
32
-git clone https://your-instance.com/username/hello-world.git
31
+git clone https://git.yourcompany.com/username/hello-world.git
33
32
```
34
33
35
-## Step 3: Making Changes
34
+For HTTPS, you'll need a Personal Access Token. Generate one at **Settings → Tokens**.
36
35
37
-Open the folder in your favorite editor (VS Code, etc.).
36
+## Step 3: Make Changes
38
37
39
38
```bash
40
39
cd hello-world
41
-```
42
40
43
-Modify `README.md`:
44
-```markdown
45
-# Hello World
46
-This is my first change!
47
-```
41
+# Edit files
42
+echo "# Hello World\n\nThis is my first change!" > README.md
48
43
49
-Commit the change:
50
-```bash
44
+# Commit
51
45
git add README.md
52
46
git commit -m "Update README"
53
47
```
54
48
55
-## Step 4: Pushing
56
-
57
-Push your changes back to OpenCodeHub:
49
+## Step 4: Push
58
50
59
51
```bash
60
52
git push origin main
61
53
```
62
-(Or use `och push` if you installed the CLI).
63
54
64
-## Step 5: Viewing Changes
55
+Refresh the repository page to see your changes.
56
+
57
+## Step 5: Create a Pull Request
58
+
59
+1. Create a feature branch:
60
+```bash
61
+git checkout -b feat/add-feature
62
+```
63
+
64
+2. Make changes and push:
65
+```bash
66
+echo "New feature" > feature.txt
67
+git add feature.txt
68
+git commit -m "Add new feature"
69
+git push -u origin feat/add-feature
70
+```
71
+
72
+3. In the web UI, go to **Pull Requests** → **New Pull Request**
73
+4. Select `feat/add-feature` as source, `main` as target
74
+5. Add a title and description
75
+6. Click **Create Pull Request**
76
+
77
+## Step 6: Review and Merge
78
+
79
+1. Add reviewers on the PR page
80
+2. Reviewers can approve or request changes
81
+3. Once approved, click **Merge** or add to the **Merge Queue**
82
+
83
+## Step 7: Explore Features
84
+
85
+### Wiki
86
+Go to the **Wiki** tab to create documentation for your project.
87
+
88
+### Issues
89
+Go to **Issues** → **New Issue** to track bugs and feature requests.
90
+
91
+### Projects
92
+Go to **Projects** to create a kanban board for task management.
93
+
94
+### Actions
95
+Go to **Actions** to view CI/CD pipeline runs. Create `.github/workflows/*.yml` files to set up automated builds.
96
+
97
+### Settings
98
+Go to **Settings** to configure:
99
+- **Branch Protection** — Require reviews, status checks
100
+- **Collaborators** — Add team members
101
+- **Webhooks** — Integrations with external services
102
+- **Deploy Keys** — SSH keys for automated deployments
65
103
66
-Refresh your repository page in the browser. You should see your updated `README.md` displayed and the latest commit listed.
104
+## Next Steps
67
105
68
-Congratulations! You've successfully hosted code on OpenCodeHub.
106
+- **[Stacked PRs](/features/stacked-prs/)** — Break large changes into reviewable stacks
107
+- **[AI Code Review](/features/ai-review/)** — Get automated code feedback
108
+- **[CI/CD](/features/ci-actions/)** — Set up automated pipelines
109
+- **[CLI](/reference/cli-overview/)** — Manage everything from the terminal
69
110
▼docs-site/src/content/docs/getting-started/installation.md
+78-37
2
2
title: "Installation Guide"
3
3
---
4
4
5
+OpenCodeHub is a self-hosted Git platform. You can run it via **Docker** (recommended) or **Node.js** (for development).
5
6
6
-OpenCodeHub allows you to host your own GitHub-like platform. You can run it via **Docker** (recommended for production) or **Node.js** (for development/custom setups).
7
-
8
-## 📋 System Requirements
7
+## System Requirements
9
8
10
9
| Component | Minimum | Recommended |
11
10
|-----------|---------|-------------|
14
13
| **Disk** | 10GB | 50GB SSD |
15
14
| **OS** | Linux (Ubuntu/Debian) | Linux |
16
15
17
-
18
-## ⚡ 1-Click Installation (Recommended)
19
-
20
-The easiest way to install OpenCodeHub on a VPS, NAS, or edge device is using the automated installation script. This script automatically handles Docker setup, environment variables, database generation, and configuring the unified `DATA_DIR`.
16
+## 1-Click Installation (Recommended)
21
17
22
18
```bash
23
19
curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash
24
20
```
25
21
26
22
The script will:
27
-1. Ensure Docker and Docker Compose are installed.
28
-2. Clone the latest stable release.
29
-3. Automatically configure a unified `/data` mount point (`DATA_DIR`) for all your databases, git repos, and storage to easily enable painless backups.
30
-4. Prompt you to create an initial Admin user account.
31
-
32
-### Manual Setup (Advanced)
23
+1. Ensure Docker and Docker Compose are installed
24
+2. Clone the latest stable release
25
+3. Configure a unified `DATA_DIR` for databases, git repos, and storage
26
+4. Prompt you to create an initial admin user
33
27
34
-If you prefer to configure Docker Compose manually:
28
+## Manual Docker Setup
35
29
36
-1. Clone the repository:
37
30
```bash
31
+# Clone
38
32
git clone https://github.com/swadhinbiswas/OpencodeHub.git
39
-cd OpenCodeHub
40
-```
33
+cd OpencodeHub
41
34
42
-2. Configure environment:
43
-```bash
35
+# Configure
44
36
cp .env.example .env
45
-```
46
-Open `.env` and set your `DATA_DIR` (e.g., `DATA_DIR=/home/user/opencodehub-data`). The system will automatically map the database, SSH keys, git repositories, and object storage paths to this unified folder.
37
+# Edit .env — at minimum, change all secrets!
47
38
48
-3. Start services:
49
-```bash
39
+# Start
50
40
docker-compose up -d
41
+
42
+# Create admin user
43
+docker-compose exec app bun run scripts/seed-admin.ts
51
44
```
52
45
53
-4. Initialize the database and admin user:
46
+The app runs on **port 4321** (HTTP) and **port 2222** (SSH git).
47
+
48
+:::caution[Change Secrets]
49
+Before going live, generate new secrets for all `*_SECRET` and `*_KEY` variables:
54
50
```bash
55
-bun run scripts/seed-admin.ts
51
+openssl rand -base64 32
56
52
```
53
+:::
57
54
55
+## Docker Compose Services
58
56
59
-## 🛠 Nginx Reverse Proxy (SSL)
57
+| Service | Port | Purpose |
58
+|---------|------|---------|
59
+| `app` | 4321, 2222 | Main application + SSH git |
60
+| `postgres` | 5432 | PostgreSQL database |
61
+| `redis` | 6379 | Redis cache/queues |
62
+| `runner` | — | CI/CD Docker runner |
60
63
61
-In production, you **must** use HTTPS. Here is a standard Nginx configuration.
64
+## Nginx Reverse Proxy (SSL)
65
+
66
+In production, use HTTPS. Standard Nginx config:
62
67
63
68
```nginx
64
69
server {
80
83
client_max_body_size 500M;
81
84
82
85
location / {
83
- proxy_pass http://localhost:3000;
86
+ proxy_pass http://localhost:4321;
84
87
proxy_set_header Host $host;
85
88
proxy_set_header X-Real-IP $remote_addr;
86
89
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
89
92
}
90
93
```
91
94
95
+See [Nginx Deployment](/administration/deploy-nginx/) for the full config with WebSocket support and Git optimizations.
92
96
93
-## ✅ Production Checklist
97
+## SSH Git Access
94
98
95
-Before going live to users:
99
+The SSH git server starts automatically on port 2222. Clone with:
96
100
97
-- [ ] **Secrets Rotated**: Default secrets replaced with strong random strings.
98
-- [ ] **HTTPS Enabled**: SSL certificate configured via Nginx/Caddy.
99
-- [ ] **Rate Limiting**: `RATE_LIMIT_*` env vars adjusted for expected load.
100
-- [ ] **Monitoring**: Grafana/Sentry configured for error tracking ([Guide](../administration/monitoring.md)).
101
+```bash
102
+git clone ssh://git@your-server:2222/owner/repo.git
103
+```
101
104
105
+The host key is auto-generated at `GIT_SSH_HOST_KEY` on first start.
102
106
103
-## Next Steps
107
+## Node.js Setup (Development)
108
+
109
+```bash
110
+# Install dependencies
111
+npm install
112
+
113
+# Configure
114
+cp .env.example .env
115
+
116
+# Push schema to database
117
+npm run db:push
118
+
119
+# Create admin user
120
+bun run scripts/seed-admin.ts
121
+
122
+# Start dev server
123
+npm run dev
124
+```
125
+
126
+See [Local Dev Setup](/development/local-dev-setup/) for details.
104
127
105
-Now that you have OpenCodeHub up and running, let's look around:
128
+## Production Checklist
129
+
130
+Before going live:
131
+
132
+- [ ] All secrets rotated (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, etc.)
133
+- [ ] HTTPS enabled via Nginx/Caddy
134
+- [ ] PostgreSQL used (not SQLite)
135
+- [ ] Redis configured for distributed locking
136
+- [ ] Rate limiting enabled (`RATE_LIMIT_ENABLED=true`)
137
+- [ ] Email configured (SMTP) for notifications
138
+- [ ] Storage backend configured (S3 recommended for multi-node)
139
+- [ ] Backup strategy configured
140
+- [ ] Monitoring set up (see [Monitoring](/administration/monitoring/))
141
+
142
+## Next Steps
106
143
107
-👉 **[Quick Start Guide](quick-start.md)**
144
+- **[Quick Start Guide](/getting-started/quick-start/)** — Navigate the dashboard and create your first repo
145
+- **[Configuration Reference](/administration/configuration/)** — All environment variables
146
+- **[Docker Deployment](/administration/deploy-docker/)** — Production Docker setup
108
147
▼docs-site/src/content/docs/getting-started/quick-start.md
+94-26
2
2
title: "Quick Start Guide"
3
3
---
4
4
5
-
6
-Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes.
5
+Welcome to OpenCodeHub! This guide will help you get started in minutes.
7
6
8
7
## 1. Login
9
-Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation.
10
-
11
-## 2. Navigating the Dashboard
12
-Your dashboard gives you a quick overview of:
13
-- **Recent Repositories**: Projects you've worked on recently.
14
-- **Your Pull Requests**: PRs requiring your attention.
15
-- **Activity Feed**: What your team is doing.
16
-
17
-## 3. Create a Repository
18
-1. Click the **+** (Plus) icon in the top right navigation bar.
19
-2. Select **New Repository**.
20
-3. Enter a **Repository Name** (e.g., `my-awesome-project`).
21
-4. Choose Visibility:
22
- - **Public**: Visible to everyone (if your instance allows it).
23
- - **Private**: Visible only to you and collaborators.
24
-5. Click **Create Repository**.
25
-
26
-## 4. Collaborate
27
-1. **Invite Members**: Go to **Settings** > **Collaborators** to add team members.
28
-2. **Create Issues**: Use the **Issues** tab to track tasks and bugs.
29
-3. **Open Pull Requests**: Push a branch and open a PR to review code.
30
8
9
+Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation.
31
10
32
-## Next Steps
11
+## 2. Create a Repository
12
+
13
+1. Click the **+** icon in the top navigation bar
14
+2. Select **New Repository**
15
+3. Enter a name (e.g., `my-project`)
16
+4. Choose visibility: **Public** or **Private**
17
+5. Optionally initialize with a README
18
+6. Click **Create Repository**
19
+
20
+## 3. Push Code
21
+
22
+```bash
23
+# Clone your new repository
24
+git clone https://git.yourcompany.com/your-username/my-project.git
25
+cd my-project
26
+
27
+# Add your code
28
+echo "# My Project" > README.md
29
+git add .
30
+git commit -m "Initial commit"
31
+git push -u origin main
32
+```
33
+
34
+Or use SSH:
35
+
36
+```bash
37
+git clone ssh://git@your-server:2222/your-username/my-project.git
38
+```
39
+
40
+## 4. Explore the Dashboard
41
+
42
+Your dashboard shows:
43
+- **Recent Repositories** you've worked on
44
+- **Pull Requests** requiring your attention
45
+- **Stacked PRs** if you're using the stack workflow
46
+- **Activity Feed** from your team
47
+
48
+## 5. Create a Pull Request
49
+
50
+1. Navigate to your repository
51
+2. Click **Pull Requests** → **New Pull Request**
52
+3. Select source and target branches
53
+4. Add a title and description
54
+5. Click **Create Pull Request**
55
+
56
+See [First Repository](/getting-started/first-repository/) for a complete walkthrough.
33
57
34
-Now that you're exploring the UI, try pushing your first code:
58
+## 6. Try Key Features
59
+
60
+### Stacked PRs
61
+Break large changes into reviewable stacks:
62
+```bash
63
+# Install the CLI
64
+npm install -g opencodehub-cli
65
+
66
+# Login
67
+och auth login
68
+
69
+# Create a stacked branch
70
+och stack create feature/part-1
71
+
72
+# Make changes, commit, push
73
+och stack submit
74
+```
75
+
76
+### AI Code Review
77
+Enable in **Settings** → **AI Review**. Configure your preferred provider (OpenAI, Anthropic, etc.) and AI will automatically review PRs for bugs and security issues.
78
+
79
+### Merge Queue
80
+Add PRs to the merge queue from the PR page. The queue ensures `main` never breaks by running CI on the merged result before pushing.
81
+
82
+### CI/CD Pipelines
83
+Create `.github/workflows/*.yml` files in your repository. OpenCodeHub uses GitHub Actions-compatible syntax.
84
+
85
+## 7. Install the CLI
86
+
87
+```bash
88
+npm install -g opencodehub-cli
89
+
90
+# Authenticate
91
+och auth login --url https://git.yourcompany.com
92
+
93
+# Check status
94
+och status
95
+```
96
+
97
+See [CLI Overview](/reference/cli-overview/) for all commands.
98
+
99
+## Next Steps
35
100
36
-👉 **[Create Your First Repository](first-repository.md)**
101
+- **[First Repository](/getting-started/first-repository/)** — Complete repository lifecycle
102
+- **[Stacked PRs](/features/stacked-prs/)** — Learn the stack workflow
103
+- **[CLI Reference](/reference/cli-commands/)** — All CLI commands
104
+- **[Configuration](/administration/configuration/)** — Customize your instance
37
105
▼docs-site/src/content/docs/guides/example.md
+0-9
1
-title: Example Guide
2
-description: A guide in my new Starlight docs site.
3
-
4
-Guides lead a user through a specific task they want to accomplish, often with a sequence of steps.
5
-Writing a good guide requires thinking about what your users are trying to do.
6
-
7
-## Further reading
8
-
9
-- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework
10
0
▼docs-site/src/content/docs/index.mdx
+103-70
1
1
---
2
2
title: OpenCodeHub Documentation
3
-description: The open-source, AI-native Git server with Stacked PRs and Smart Merge Queues.
3
+description: The open-source, self-hosted Git platform with Stacked PRs, AI Code Review, and Smart Merge Queues.
4
4
hero:
5
5
title: OpenCodeHub
6
- tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, and a modern developer experience.
6
+ tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, AI code review, and a modern developer experience.
7
7
---
8
8
9
9
import { Card, CardGrid } from "@astrojs/starlight/components";
11
11
## Why OpenCodeHub?
12
12
13
13
<CardGrid stagger>
14
- <Card title="🚀 Stacked PRs" icon="rocket">
15
- Break large features into small, dependent steps. Ship faster without the
16
- review bottleneck.
14
+ <Card title="Stacked PRs" icon="rocket">
15
+ Break large features into small, dependent steps. Graphite-style stacked branches with automatic rebase and merge readiness checks.
17
16
</Card>
18
- <Card title="🤖 AI Code Review" icon="star">
19
- Built-in LLM agents (GPT-4/Claude) review your code for bugs and security
20
- issues automatically.
17
+ <Card title="AI Code Review" icon="star">
18
+ Built-in LLM agents (GPT-4, Claude, Gemini, Groq, Ollama, and 10+ providers) review your code for bugs and security issues automatically.
21
19
</Card>
22
- <Card title="🔀 Smart Merge Queue" icon="random">
23
- Automate your merging strategy. Ensure `main` never breaks with speculative
24
- CI runs.
20
+ <Card title="Smart Merge Queue" icon="random">
21
+ Stack-aware merge queue with speculative CI builds, priority lanes, and conflict detection. Ensure `main` never breaks.
25
22
</Card>
26
- <Card title="🏠 Fully Self-Hosted" icon="laptop">
27
- Keep your code on your servers. No "per-seat" pricing. No vendor lock-in.
23
+ <Card title="CI/CD Pipelines" icon="add-document">
24
+ GitHub Actions-compatible workflow engine with Docker-based runners, artifact storage, and secrets management.
25
+ </Card>
26
+ <Card title="Fully Self-Hosted" icon="laptop">
27
+ Keep your code on your servers. No per-seat pricing. No vendor lock-in. Deploy with Docker, Podman, or bare metal.
28
+ </Card>
29
+ <Card title="Package Registry" icon="seti:db">
30
+ Built-in npm and Docker container registry. Host private packages alongside your source code.
28
31
</Card>
29
32
</CardGrid>
30
33
31
-## Architecture Overview
32
-
33
-![OpenCodeHub Architecture](/architecture.svg)
34
+## Core Capabilities
34
35
35
-OpenCodeHub is built on a modern, scalable architecture designed for performance and flexibility.
36
+<CardGrid>
37
+ <Card title="Git Hosting" icon="seti:git">
38
+ Full Git support with HTTP smart protocol and SSH. Repository forking, mirroring, LFS, and CODEOWNERS enforcement.
39
+ </Card>
40
+ <Card title="Pull Requests" icon="seti:code">
41
+ Full PR lifecycle with inline comments, file-level approvals, review templates, reviewer routing rules, and auto-merge.
42
+ </Card>
43
+ <Card title="Issues & Projects" icon="seti:folder">
44
+ Issue tracking with custom fields and statuses. Drag-and-drop project boards with columns and cards.
45
+ </Card>
46
+ <Card title="Security" icon="seti:lock">
47
+ Secret scanning, vulnerability scanning, security policies, path-level permissions, and audit logging.
48
+ </Card>
49
+ <Card title="Webhooks & Automations" icon="seti:webhook">
50
+ Outbound webhooks with HMAC signing. Rule-based automations for PR workflows, reviews, and deployments.
51
+ </Card>
52
+ <Card title="Organizations & Teams" icon="seti:user">
53
+ Multi-tenant organizations with role-based access control, team management, and fine-grained permissions.
54
+ </Card>
55
+</CardGrid>
36
56
37
57
## Deploy Anywhere
38
58
39
59
<CardGrid>
40
- <Card title="🐳 Docker" icon="add-document">
41
- The recommended way to deploy. Full Docker Compose setup with PostgreSQL and
42
- Redis. [Deploy with Docker →](/administration/deploy-docker/)
60
+ <Card title="Docker" icon="add-document">
61
+ The recommended way to deploy. Full Docker Compose setup with PostgreSQL and Redis.
62
+ [Deploy with Docker →](/administration/deploy-docker/)
43
63
</Card>
44
- <Card title="🚀 Coolify" icon="rocket">
45
- Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL
46
- and managed databases. [Deploy on Coolify →](/administration/deploy-coolify/)
64
+ <Card title="Podman" icon="add-document">
65
+ Rootless containers with native systemd integration. Ideal for security-focused deployments.
66
+ [Deploy with Podman →](/administration/deploy-podman/)
47
67
</Card>
48
- <Card title="🖥️ CyberPanel" icon="laptop">
49
- Modern hosting panel with OpenLiteSpeed. Docker or native Node.js deployment.
50
- [Deploy on CyberPanel →](/administration/deploy-cyberpanel/)
68
+ <Card title="Kubernetes" icon="add-document">
69
+ Deploy to Kubernetes using the bundled Helm chart with horizontal scaling.
70
+ [Deploy on Kubernetes →](/administration/kubernetes/)
51
71
</Card>
52
- <Card title="📁 cPanel" icon="seti:folder">
53
- Deploy on shared hosting using Node.js or PM2. Best for budget hosting.
54
- [Deploy on cPanel →](/administration/deploy-cpanel/)
72
+ <Card title="Coolify" icon="rocket">
73
+ Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL.
74
+ [Deploy on Coolify →](/administration/deploy-coolify/)
55
75
</Card>
56
- <Card title="💾 NAS" icon="seti:db">
76
+ <Card title="NAS" icon="seti:db">
57
77
Run on Synology, TrueNAS, or QNAP. Perfect for private home/team Git servers.
58
78
[Deploy on NAS →](/administration/deploy-nas/)
59
79
</Card>
60
- <Card title="🦭 Podman" icon="add-document">
61
- Rootless containers with native systemd integration. Ideal for
62
- security-focused deployments. [Deploy with Podman
63
- →](/administration/deploy-podman/)
64
- </Card>
65
- <Card title="☁️ Cloudflare" icon="add-document">
66
- Use Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage.
80
+ <Card title="Cloudflare" icon="add-document">
81
+ Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage.
67
82
[Deploy with Cloudflare →](/administration/deploy-cloudflare/)
68
83
</Card>
69
- <Card title="🌐 Nginx" icon="add-document">
70
- Production-ready reverse proxy config with SSL and Git-specific
71
- optimizations. [Configure Nginx →](/administration/deploy-nginx/)
84
+ <Card title="Nginx" icon="add-document">
85
+ Production-ready reverse proxy config with SSL and Git-specific optimizations.
86
+ [Configure Nginx →](/administration/deploy-nginx/)
87
+ </Card>
88
+ <Card title="cPanel / CyberPanel" icon="seti:folder">
89
+ Deploy on shared hosting or VPS panels using Node.js + PM2.
90
+ [cPanel →](/administration/deploy-cpanel/) | [CyberPanel →](/administration/deploy-cyberpanel/)
72
91
</Card>
73
92
</CardGrid>
74
93
75
94
## Tech Stack
76
95
77
-| Layer | Technology |
78
-| ------------ | ------------------------------------- |
79
-| **Frontend** | Astro, React, Tailwind CSS, shadcn/ui |
80
-| **Backend** | Node.js, Astro API Routes, simple-git |
81
-| **Database** | PostgreSQL, Turso (LibSQL), SQLite |
82
-| **Storage** | Local disk, S3, R2 |
83
-| **Auth** | JWT, 2FA, OAuth (GitHub, Google) |
84
-| **CI/CD** | GitHub Actions compatible runners |
96
+| Layer | Technology |
97
+| --- | --- |
98
+| **Framework** | Astro 4.x (SSR, Node standalone adapter) |
99
+| **Frontend** | React 18, Tailwind CSS, Radix UI, Framer Motion |
100
+| **Backend** | Astro API Routes (175+ endpoints), GraphQL |
101
+| **Database** | PostgreSQL (prod), SQLite (dev), Turso/LibSQL (edge) |
102
+| **ORM** | Drizzle ORM (38 schema tables) |
103
+| **Auth** | JWT, bcryptjs, TOTP 2FA, OAuth (Google, GitHub), SSO/SAML, OIDC |
104
+| **Git** | simple-git, isomorphic-git, nodegit, ssh2 |
105
+| **Storage** | Local filesystem or S3-compatible (AWS, MinIO, R2, Garage, Wasabi) |
106
+| **CI/CD** | GitHub Actions-compatible engine, Docker-based runners |
107
+| **AI** | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez |
108
+| **Queue** | BullMQ + Redis |
109
+| **Observability** | OpenTelemetry, Prometheus metrics, Pino logging |
110
+
111
+## Quick Start
112
+
113
+```bash
114
+# Clone and start with Docker Compose
115
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
116
+cd OpencodeHub
117
+cp .env.example .env # Edit secrets!
118
+docker-compose up -d
119
+
120
+# Create admin user
121
+docker-compose exec app bun run scripts/seed-admin.ts
122
+
123
+# Open in browser
124
+open http://localhost:4321
125
+```
126
+
127
+:::tip[SSH Git Access]
128
+The SSH git server starts automatically on port 2222. Clone with:
129
+`git clone ssh://git@localhost:2222/owner/repo.git`
130
+:::
85
131
86
132
## Quick Links
87
133
88
134
<CardGrid>
89
135
<Card title="Installation" icon="add-document">
90
- Get OpenCodeHub running in minutes using our 1-click install script. [Read the guide
91
- →](/getting-started/installation/)
136
+ Get OpenCodeHub running in minutes. [Read the guide →](/getting-started/installation/)
92
137
</Card>
93
- <Card title="CircleCI Integration" icon="seti:check">
94
- Connect CircleCI as your external CI provider. Trigger builds and report
95
- PR checks automatically. [Set up CircleCI →](/guides/circleci-integration/)
138
+ <Card title="Configuration" icon="seti:config">
139
+ All environment variables and settings. [View config →](/administration/configuration/)
96
140
</Card>
97
141
<Card title="CLI Reference" icon="seti:shell">
98
- Learn the `och` CLI commands for managing stacks and repositories. [View
99
- commands →](/reference/cli-commands/)
142
+ The `och` CLI with 20+ command groups. [View commands →](/reference/cli-commands/)
100
143
</Card>
101
144
<Card title="API Reference" icon="open-book">
102
- Integrate with OpenCodeHub using the REST API. [Explore the API
103
- →](/api/rest-api/)
145
+ REST API with 175+ endpoints. [Explore the API →](/api/rest-api/)
146
+ </Card>
147
+ <Card title="Architecture" icon="seti:code">
148
+ System design, data flow, and component overview. [View architecture →](/development/architecture/)
104
149
</Card>
105
150
<Card title="Contributing" icon="github">
106
- Help make OpenCodeHub better. Set up your local dev environment. [Start
107
- contributing →](/development/contributing/)
151
+ Help make OpenCodeHub better. [Start contributing →](/development/contributing/)
108
152
</Card>
109
153
</CardGrid>
110
-
111
-## Storage Options
112
-
113
-:::tip[Performance Recommendation]
114
-For best performance, use **local SSD storage** via `DATA_DIR` in production. Object storage (S3/R2) introduces latency. See [Storage Adapters](/guides/storage-adapters/#️-performance-considerations) for details.
115
-:::
116
-
117
-| Storage Type | Performance | Cost | Best For |
118
-| ---------------- | ----------- | ---- | ----------------------- |
119
-| **Local SSD** | ⚡ Fastest | $$ | Production, self-hosted |
120
-| **S3/R2** | 🐢 Slow | $ | Backup, multi-node |
121
154
▼docs-site/src/content/docs/reference/api-versioning.md
+14-0
1
+# API Versioning
2
+
3
+OpenCodeHub provides a stable REST API and GraphQL endpoint.
4
+
5
+## Versioning Strategy
6
+Currently, the REST API does not have explicit `/v1/` prefixes in the route paths. Instead, the API follows a continuous evolution model where breaking changes are heavily minimized.
7
+
8
+When breaking changes are unavoidable:
9
+- They will be announced in the release notes.
10
+- A deprecation header (`Deprecation: <date>`) will be added to the response of the old endpoint.
11
+- Clients will have at least one minor release cycle to migrate.
12
+
13
+## GraphQL
14
+The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes.
0
15
▼docs-site/src/content/docs/reference/cli-auth-config.md
+2-2
10
10
### Login
11
11
12
12
```bash
13
-och auth login --url http://localhost:3000
13
+och auth login --url http://localhost:4321
14
14
```
15
15
16
16
Options:
17
17
18
-- `--url <url>`: Instance URL (defaults to `http://localhost:3000`)
18
+- `--url <url>`: Instance URL (defaults to `http://localhost:4321`)
19
19
- `--with-token`: Prompt for a personal access token
20
20
- `--token <token>`: Non‑interactive token login
21
21
22
22
▼docs-site/src/content/docs/reference/cli-core-commands.md
+1-1
16
16
Shorthand:
17
17
18
18
```bash
19
-och init --url http://localhost:3000
19
+och init --url http://localhost:4321
20
20
och push --branch main
21
21
```
22
22
23
23
▼docs-site/src/content/docs/reference/example.md
+0-9
1
-title: Example Reference
2
-description: A reference page in my new Starlight docs site.
3
-
4
-Reference pages are ideal for outlining how things work in terse and clear terms.
5
-Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you're documenting.
6
-
7
-## Further reading
8
-
9
-- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework
10
0
▼docs-site/src/content/docs/tutorials/zero-to-production.md
+1-1
34
34
npm run dev
35
35
```
36
36
37
-Open the app at `http://localhost:3000` and log in.
37
+Open the app at `http://localhost:4321` and log in.
38
38
39
39
## 5) Production deployment
40
40
41
41
▼docs-site/src/custom.css
+4-4
11
11
}
12
12
13
13
:root {
14
- --och-accent-1: #2dd4bf;
15
- --och-accent-2: #8be9fd;
16
- --och-accent-3: #bd93f9;
17
- --och-accent-4: #ff79c6;
14
+ --och-accent-1: #0d9488;
15
+ --och-accent-2: #38bdf8;
16
+ --och-accent-3: #a78bfa;
17
+ --och-accent-4: #f472b6;
18
18
19
19
--och-hero-surface: color-mix(in srgb, var(--sl-color-bg) 92%, transparent);
20
20
}
21
21
▼docs/FREE-DEPLOYMENT.md
+395-0
1
+# OpenCodeHub — Free Deployment Options
2
+
3
+> Deploy OpenCodeHub for free with persistent file storage for git repositories.
4
+
5
+---
6
+
7
+## Quick Comparison
8
+
9
+| Platform | Cost | Storage | RAM | CPU | Best For |
10
+|----------|------|---------|-----|-----|----------|
11
+| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall |
12
+| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused |
13
+| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing |
14
+| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage |
15
+| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo |
16
+| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid |
17
+
18
+---
19
+
20
+## Option 1: Oracle Cloud Free Tier (Recommended)
21
+
22
+**Always Free** — No credit card charge, no time limit.
23
+
24
+### What You Get
25
+- 4 ARM cores (Ampere A1)
26
+- 24 GB RAM
27
+- 200 GB boot volume
28
+- 2 x 1TB block volumes (total 2TB)
29
+- Free PostgreSQL database
30
+- Free Redis
31
+
32
+### Steps
33
+
34
+```bash
35
+# 1. Sign up at cloud.oracle.com
36
+# 2. Create a VM instance:
37
+# - Name: opencodehub
38
+# - Image: Ubuntu 22.04
39
+# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM)
40
+# - Upload SSH key
41
+# - Create boot volume: 100 GB
42
+
43
+# 3. SSH into your instance
44
+ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP
45
+
46
+# 4. Install Docker
47
+curl -fsSL https://get.docker.com -o get-docker.sh
48
+sudo sh get-docker.sh
49
+sudo usermod -aG docker $USER
50
+
51
+# 5. Install Docker Compose
52
+sudo apt install docker-compose-plugin -y
53
+
54
+# 6. Clone OpenCodeHub
55
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
56
+cd OpencodeHub
57
+
58
+# 7. Configure
59
+cp .env.example .env
60
+nano .env # Edit secrets
61
+
62
+# 8. Start
63
+sudo docker compose up -d
64
+
65
+# 9. Create admin
66
+sudo docker compose exec app bun run scripts/seed-admin.ts
67
+```
68
+
69
+### Access Your Instance
70
+```
71
+Web: http://YOUR_PUBLIC_IP:4321
72
+SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git
73
+HTTPS: Set up Nginx + Let's Encrypt (free)
74
+```
75
+
76
+---
77
+
78
+## Option 2: Home Server / NAS
79
+
80
+**$0 forever** — Use hardware you already own.
81
+
82
+### What You Need
83
+- Any computer running Linux/Mac/Windows
84
+- 2GB+ RAM recommended
85
+- 20GB+ free disk space
86
+
87
+### Quick Start (Linux/Mac)
88
+
89
+```bash
90
+# Install Docker
91
+curl -fsSL https://get.docker.com | sh
92
+
93
+# Clone and start
94
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
95
+cd OpencodeHub
96
+cp .env.example .env
97
+nano .env # Set secrets
98
+docker compose up -d
99
+
100
+# Create admin
101
+docker compose exec app bun run scripts/seed-admin.ts
102
+```
103
+
104
+### NAS Deployment
105
+
106
+**Synology / TrueNAS / QNAP:**
107
+1. Install Docker from Package Center
108
+2. Use Docker Compose or Portainer
109
+3. Mount a shared folder for `/data`
110
+
111
+### Access from Anywhere
112
+- **Tailscale** (free): `tailscale up` → access via Tailscale IP
113
+- **Cloudflare Tunnel** (free): Expose without opening ports
114
+- **Port forwarding**: Open port 4321 on your router
115
+
116
+---
117
+
118
+## Option 3: Fly.io Free Tier
119
+
120
+**Free** — 3GB persistent volume included.
121
+
122
+### Steps
123
+
124
+```bash
125
+# 1. Install flyctl
126
+curl -L https://fly.io/install.sh | sh
127
+
128
+# 2. Login
129
+fly auth login
130
+
131
+# 3. Launch
132
+fly launch --name opencodehub
133
+
134
+# 4. Add persistent volume
135
+fly volume create opencodehub_data --region sjc --size 3
136
+
137
+# 5. Set secrets
138
+fly secrets set JWT_SECRET=$(openssl rand -hex 32)
139
+fly secrets set SESSION_SECRET=$(openssl rand -hex 32)
140
+fly secrets set DATABASE_URL="postgresql://..."
141
+fly secrets set REDIS_URL="redis://..."
142
+
143
+# 6. Deploy
144
+fly deploy
145
+```
146
+
147
+### Limitations
148
+- 3GB storage (enough for small repos)
149
+- 256MB RAM (may be slow for large repos)
150
+- Apps休眠 after inactivity (cold starts take ~30s)
151
+
152
+---
153
+
154
+## Option 4: Google Cloud Free Tier
155
+
156
+**Always Free** — e2-micro instance.
157
+
158
+### Steps
159
+
160
+```bash
161
+# 1. Sign up at cloud.google.com
162
+# 2. Create e2-micro VM:
163
+# - Machine type: e2-micro (0.25 vCPU, 1GB RAM)
164
+# - Boot disk: 30GB SSD (free)
165
+# - Firewall: Allow HTTP (80), SSH (22)
166
+
167
+# 3. SSH in and install Docker
168
+ssh -i ~/.ssh/key USER@EXTERNAL_IP
169
+sudo apt update && sudo apt install docker.io docker-compose-plugin -y
170
+
171
+# 4. Deploy OpenCodeHub
172
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
173
+cd OpencodeHub
174
+cp .env.example .env
175
+nano .env
176
+docker compose up -d
177
+```
178
+
179
+### Limitations
180
+- Only 1GB RAM (tight for production)
181
+- 0.25 vCPU (slow builds)
182
+- Good for personal/light use
183
+
184
+---
185
+
186
+## Option 5: Self-Hosted on Existing VPS
187
+
188
+If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.):
189
+
190
+```bash
191
+# SSH into your server
192
+ssh root@YOUR_SERVER_IP
193
+
194
+# One-line install
195
+curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash
196
+```
197
+
198
+---
199
+
200
+## Option 6: Docker on Windows/Mac
201
+
202
+For local development or personal use:
203
+
204
+```bash
205
+# Install Docker Desktop for Windows/Mac
206
+# https://docs.docker.com/get-docker/
207
+
208
+# Clone and start
209
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
210
+cd OpencodeHub
211
+cp .env.example .env
212
+nano .env
213
+docker compose up -d
214
+
215
+# Create admin
216
+docker compose exec app bun run scripts/seed-admin.ts
217
+```
218
+
219
+---
220
+
221
+## Storage Configuration
222
+
223
+### Local Storage (Default)
224
+```bash
225
+STORAGE_TYPE=local
226
+STORAGE_PATH=/data/storage
227
+GIT_REPOS_PATH=/data/repositories
228
+```
229
+
230
+### S3-Compatible (MinIO on same server)
231
+```bash
232
+# Add to docker-compose.yml:
233
+minio:
234
+ image: minio/minio
235
+ command: server /data --console-address ":9001"
236
+ volumes:
237
+ - ./data/minio:/data
238
+ ports:
239
+ - "9001:9001"
240
+
241
+STORAGE_TYPE=s3
242
+STORAGE_ENDPOINT=http://minio:9000
243
+STORAGE_BUCKET=opencodehub
244
+STORAGE_ACCESS_KEY_ID=minioadmin
245
+STORAGE_SECRET_ACCESS_KEY=minioadmin
246
+```
247
+
248
+---
249
+
250
+## HTTPS Setup (Free)
251
+
252
+### With Let's Encrypt + Nginx
253
+
254
+```bash
255
+# Install Nginx and Certbot
256
+sudo apt install nginx certbot python3-certbot-nginx -y
257
+
258
+# Create Nginx config
259
+sudo nano /etc/nginx/sites-available/opencodehub
260
+```
261
+
262
+```nginx
263
+server {
264
+ listen 80;
265
+ server_name git.yourdomain.com;
266
+ return 301 https://$host$request_uri;
267
+}
268
+
269
+server {
270
+ listen 443 ssl http2;
271
+ server_name git.yourdomain.com;
272
+
273
+ ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
274
+ ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
275
+
276
+ client_max_body_size 500M;
277
+
278
+ location / {
279
+ proxy_pass http://localhost:4321;
280
+ proxy_set_header Host $host;
281
+ proxy_set_header X-Real-IP $remote_addr;
282
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
283
+ proxy_set_header X-Forwarded-Proto $scheme;
284
+ }
285
+
286
+ # WebSocket support
287
+ location /api/realtime {
288
+ proxy_pass http://localhost:4321;
289
+ proxy_http_version 1.1;
290
+ proxy_set_header Upgrade $http_upgrade;
291
+ proxy_set_header Connection "upgrade";
292
+ }
293
+}
294
+```
295
+
296
+```bash
297
+# Enable and get SSL
298
+sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/
299
+sudo certbot --nginx -d git.yourdomain.com
300
+```
301
+
302
+---
303
+
304
+## Environment Variables (Production)
305
+
306
+```bash
307
+# Generate all secrets
308
+JWT_SECRET=$(openssl rand -hex 32)
309
+SESSION_SECRET=$(openssl rand -hex 32)
310
+INTERNAL_HOOK_SECRET=$(openssl rand -hex 32)
311
+CRON_SECRET=$(openssl rand -hex 32)
312
+RUNNER_SECRET=$(openssl rand -hex 32)
313
+AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32)
314
+WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32)
315
+
316
+# Your .env file
317
+DATABASE_DRIVER=postgres
318
+DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub
319
+DATABASE_SSL=false
320
+REDIS_URL=redis://localhost:6379
321
+SITE_URL=https://git.yourdomain.com
322
+STORAGE_TYPE=local
323
+GIT_REPOS_PATH=/data/repositories
324
+NODE_ENV=production
325
+```
326
+
327
+---
328
+
329
+## Backup Strategy
330
+
331
+### Automated Backup Script
332
+```bash
333
+#!/bin/bash
334
+# backup.sh — Run daily via cron
335
+BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
336
+mkdir -p "$BACKUP_DIR"
337
+
338
+# Database dump
339
+docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql"
340
+
341
+# Git repos
342
+tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/
343
+
344
+# Upload to S3 (optional)
345
+# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/
346
+```
347
+
348
+### Cron Job
349
+```bash
350
+# Add to crontab
351
+crontab -e
352
+0 2 * * * /path/to/backup.sh
353
+```
354
+
355
+---
356
+
357
+## Monitoring
358
+
359
+### Health Check
360
+```bash
361
+# Check if app is running
362
+curl -f http://localhost:4321/api/health || echo "DOWN"
363
+
364
+# Check Docker status
365
+docker compose ps
366
+docker compose logs --tail=50 app
367
+```
368
+
369
+### Uptime Robot (Free)
370
+1. Sign up at uptimerobot.com
371
+2. Add HTTP monitor: `http://YOUR_IP:4321/api/health`
372
+3. Get notified if your instance goes down
373
+
374
+---
375
+
376
+## Recommended Free Setup
377
+
378
+For a **personal Git server** that stores all your code:
379
+
380
+| Component | Choice |
381
+|-----------|--------|
382
+| **Cloud** | Oracle Cloud Free Tier |
383
+| **Server** | ARM A1 (4 cores, 24GB RAM) |
384
+| **Storage** | 100GB boot + 1TB block volume |
385
+| **Database** | PostgreSQL (Docker) |
386
+| **Cache** | Redis (Docker) |
387
+| **HTTPS** | Nginx + Let's Encrypt |
388
+| **Access** | Tailscale (private) or port forwarding |
389
+| **Backups** | Cron script + local S3 |
390
+
391
+**Total cost: $0/month forever.**
392
+
393
+---
394
+
395
+*Generated for OpenCodeHub free deployment.*
0
396
▼docs/RENDER-DEPLOYMENT.md
+163-0
1
+# OpenCodeHub — Render Deployment Guide
2
+
3
+> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month.
4
+
5
+---
6
+
7
+## What You Get
8
+
9
+- OpenCodeHub running in Asia (Singapore)
10
+- Free PostgreSQL database
11
+- Free Redis via Upstash
12
+- 1GB persistent storage for git repos
13
+- HTTPS included
14
+- Admin account
15
+
16
+---
17
+
18
+## Step 1: Create Upstash Redis (2 min)
19
+
20
+1. Go to **https://upstash.com** → Sign up (free)
21
+2. Click **Create Database**
22
+3. Settings:
23
+ - **Name**: `opencodehub`
24
+ - **Region**: **Asia Pacific (Singapore)**
25
+ - **Plan**: Pay as you go (free tier: 10K commands/day)
26
+4. Click **Create**
27
+5. Copy the **Redis URL** (looks like `rediss://...@...`)
28
+6. Keep this tab open — you'll need it in Step 3
29
+
30
+---
31
+
32
+## Step 2: Deploy to Render (5 min)
33
+
34
+1. Go to **https://render.com** → Sign up (free)
35
+2. Click **New** → **Blueprint**
36
+3. Connect your GitHub account
37
+4. Select your OpenCodeHub repository
38
+5. Render detects `render.yaml` and shows:
39
+ - `opencodehub-db` (PostgreSQL — Singapore)
40
+ - `opencodehub` (Web Service — Singapore)
41
+6. Click **Apply**
42
+7. Wait for deployment (~3-5 min first time)
43
+
44
+---
45
+
46
+## Step 3: Set Redis URL (1 min)
47
+
48
+1. Go to your Web Service → **Environment** tab
49
+2. Find `REDIS_URL` (shows "Not set")
50
+3. Click the pencil icon → Paste your **Upstash Redis URL**
51
+4. Click **Save**
52
+
53
+---
54
+
55
+## Step 4: Set Site URL (1 min)
56
+
57
+1. In the same **Environment** tab
58
+2. Find `SITE_URL` (shows "Not set")
59
+3. Click the pencil icon → Enter your Render URL:
60
+ ```
61
+ https://opencodehub.onrender.com
62
+ ```
63
+ (Replace with your actual URL from the service overview)
64
+4. Click **Save**
65
+
66
+---
67
+
68
+## Step 5: Manual Deploy
69
+
70
+1. Go to your Web Service → **Manual Deploy**
71
+2. Click **Deploy latest commit**
72
+3. Wait for build to complete (~2-3 min)
73
+4. Click the **URL** at the top to open your instance
74
+
75
+---
76
+
77
+## Step 6: Create Admin Account
78
+
79
+You'll see the **Setup Wizard**. Fill in:
80
+
81
+| Field | Value |
82
+|-------|-------|
83
+| Username | `admin` (or your choice) |
84
+| Email | Your email |
85
+| Password | Strong password |
86
+
87
+Click **Create Account** — you're now logged in as admin!
88
+
89
+---
90
+
91
+## Step 7: Test It
92
+
93
+```bash
94
+# Create a repo via web UI, then clone:
95
+git clone https://opencodehub.onrender.com/your-username/my-repo.git
96
+
97
+# Push code:
98
+cd my-repo
99
+echo "# Hello" > README.md
100
+git add . && git commit -m "Initial"
101
+git push origin main
102
+```
103
+
104
+---
105
+
106
+## Done! Summary
107
+
108
+| Component | Service | Region | Cost |
109
+|-----------|---------|--------|------|
110
+| App | Render Free | Singapore | $0 |
111
+| PostgreSQL | Render Free | Singapore | $0 |
112
+| Redis | Upstash Free | Singapore | $0 |
113
+| Storage | Render Disk 1GB | Singapore | $0 |
114
+| **Total** | | | **$0/month** |
115
+
116
+---
117
+
118
+## Keep Alive (Optional)
119
+
120
+Render free tier spins down after 15min inactivity. To prevent:
121
+
122
+1. Go to **https://uptimerobot.com** → Sign up (free)
123
+2. Add Monitor → Type: HTTP(s)
124
+3. URL: `https://opencodehub.onrender.com/api/health`
125
+4. Monitoring interval: 5 minutes
126
+5. Save — your app stays awake
127
+
128
+---
129
+
130
+## Upgrade Path (When You Grow)
131
+
132
+| Need | Upgrade | Cost |
133
+|------|---------|------|
134
+| Always-on (no spin-down) | Render Starter | $7/mo |
135
+| More storage (10GB+) | Render Disk upgrade | $1-7/mo |
136
+| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K |
137
+| PostgreSQL expiry | Render Starter DB | $7/mo |
138
+
139
+---
140
+
141
+## Troubleshooting
142
+
143
+**App won't start?**
144
+- Check **Logs** tab in Render
145
+- Verify `REDIS_URL` is set correctly
146
+- Ensure `SITE_URL` matches your Render URL
147
+
148
+**Redis connection failed?**
149
+- Verify Redis URL from Upstash (starts with `rediss://`)
150
+- Check Upstash dashboard shows "Active"
151
+- Ensure region is Singapore
152
+
153
+**Git clone fails?**
154
+- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git`
155
+- SSH not available on Render free tier (use HTTPS)
156
+
157
+**Slow first request?**
158
+- Normal for free tier (cold start ~30s)
159
+- Add UptimeRobot ping to keep it warm
160
+
161
+---
162
+
163
+*OpenCodeHub — Deployed on Render, Asia Region, $0/month.*
0
164
▼docs/administration/air-gapped.md
+33-0
1
+# Air-gapped Environments
2
+
3
+OpenCodeHub can be deployed in fully offline, air-gapped networks.
4
+
5
+## Prerequisites
6
+- A local container registry to host the OpenCodeHub Docker image.
7
+- A local NPM/mirror registry if you plan to build from source.
8
+
9
+## Deployment Steps
10
+1. On a machine with internet access, pull the required images:
11
+ ```bash
12
+ docker pull ghcr.io/swadhinbiswas/opencodehub:latest
13
+ docker pull postgres:15-alpine
14
+ docker pull redis:7-alpine
15
+ ```
16
+2. Save the images to tar archives:
17
+ ```bash
18
+ docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar
19
+ docker save postgres:15-alpine -o pg.tar
20
+ docker save redis:7-alpine -o redis.tar
21
+ ```
22
+3. Transfer the archives to the air-gapped network via secure media.
23
+4. Load the images on the target machine:
24
+ ```bash
25
+ docker load -i och.tar
26
+ docker load -i pg.tar
27
+ docker load -i redis.tar
28
+ ```
29
+
30
+## Configuration Adjustments
31
+- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider.
32
+- Disable outbound webhooks pointing to external SaaS platforms.
33
+- Ensure the AI Code Review module is configured to use a `local` provider (e.g., an internal Ollama instance) rather than cloud APIs like OpenAI.
0
34
▼docs/administration/backup-restore.md
+29-0
1
+# Backup and Restore
2
+
3
+This guide covers how to back up and restore an OpenCodeHub instance.
4
+
5
+## Backup Strategy
6
+
7
+### 1. Database
8
+For PostgreSQL, use `pg_dump`:
9
+```bash
10
+pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql
11
+```
12
+
13
+### 2. Git Repositories
14
+The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`).
15
+You can back this up using `rsync` or `tar`:
16
+```bash
17
+tar -czvf repos_backup.tar.gz ./data/repos/
18
+```
19
+
20
+### 3. Object Storage
21
+If you are using S3 or another object storage provider for artifacts, ensure you have enabled versioning and standard bucket backups through your cloud provider. If using local storage, back up the configured `./data/storage` directory.
22
+
23
+## Restore Strategy
24
+To restore:
25
+1. Stop the application and worker processes.
26
+2. Restore the database using `psql`.
27
+3. Extract the Git repositories to `GIT_REPOS_PATH`.
28
+4. Restore object storage data.
29
+5. Restart the application.
0
30
▼docs/administration/configuration.md
+7-2
6
6
7
7
| Variable | Description | Required | Default |
8
8
|----------|-------------|----------|---------|
9
-| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` |
10
-| `PORT` | Port to listen on | No | `3000` |
9
+| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:4321` |
10
+| `PORT` | Port to listen on | No | `4321` |
11
11
| `NODE_ENV` | `development` or `production` | No | `development` |
12
12
13
13
## Security (Critical)
17
17
| `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` |
18
18
| `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` |
19
19
| `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` |
20
+| `CRON_SECRET` | Secures cron endpoints | `openssl rand -hex 32` |
21
+| `RUNNER_SECRET` | Shared secret between server and CI runners | `openssl rand -hex 32` |
22
+| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider keys at rest | `openssl rand -hex 32` |
23
+| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts workflow/repo secrets at rest | `openssl rand -hex 32` |
24
+| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public | `openssl rand -hex 32` |
20
25
21
26
## Database
22
27
23
28
▼docs/administration/deploy-nas.md
+209-13
4
4
5
5
Deploy OpenCodeHub on your **Network Attached Storage (NAS)** device. This is perfect for small teams, personal use, or as a private Git server at home. Works on Synology DSM, TrueNAS SCALE, QNAP QTS, and any NAS supporting Docker.
6
6
7
+> **Recommended:** the repository ships `docker-compose.nas.yml`, which is
8
+> the production-safe version of every snippet below. It runs the web app,
9
+> the **SSH Git server**, and the **background worker** as separate
10
+> containers, requires secrets instead of defaulting them, and binds every
11
+> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) —
12
+> perfect for a dedicated hard disk or NAS share:
13
+>
14
+> ```bash
15
+> cp .env.example .env # then fill in ALL secrets
16
+> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d
17
+> ```
18
+>
19
+> The inline YAML in this guide is a minimal reference. Note that the **web
20
+> app container does not run SSH Git** — if you use a snippet as-is, port
21
+> 2222 will not serve SSH. Add the `ssh-git` service (or use
22
+> `docker-compose.nas.yml`) for SSH access.
23
+
7
24
---
8
25
9
26
## Prerequisites
135
152
--link opencodehub-postgres:postgres \
136
153
--link opencodehub-redis:redis \
137
154
-p 4321:4321 \
138
- -p 2222:2222 \
139
155
-e NODE_ENV=production \
140
156
-e DATABASE_URL=postgresql://opencodehub:[email protected]:5432/opencodehub \
141
157
-e REDIS_URL=redis://:[email protected]:6379 \
145
161
-e CRON_SECRET=$(openssl rand -hex 32) \
146
162
-e RUNNER_SECRET=$(openssl rand -hex 32) \
147
163
-e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \
164
+ -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \
148
165
-e SITE_URL=https://git.yourddns.net \
149
166
-e STORAGE_TYPE=local \
150
167
-e STORAGE_PATH=/data/storage \
151
- -e REPOS_PATH=/data/repos \
152
- -e SSH_HOST_KEY_PATH=/data/ssh/host_key \
168
+ -e GIT_REPOS_PATH=/data/repos \
169
+ -e GIT_SSH_HOST_KEY=/data/ssh/host_key \
153
170
-v /volume1/opencodehub/repos:/data/repos \
154
171
-v /volume1/opencodehub/storage:/data/storage \
155
172
-v /volume1/opencodehub/ssh:/data/ssh \
156
173
-v /volume1/opencodehub/cache:/data/cache \
157
- swadhinbiswas/opencodehub:latest
174
+ opencodehub/opencodehub:latest
158
175
```
159
176
160
177
> **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`).
178
+>
179
+> **SSH Git:** the app container serves the web UI only. For SSH access,
180
+> run a second container from the same image with `PROCESS_TYPE=ssh` and
181
+> expose port 2222 (this is exactly what `docker-compose.nas.yml` does).
161
182
162
183
#### Option B: Build from Source
163
184
175
196
restart: unless-stopped
176
197
ports:
177
198
- "4321:4321"
178
- - "2222:2222"
179
199
environment:
180
200
- NODE_ENV=production
201
+ - PROCESS_TYPE=app
181
202
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
182
203
- REDIS_URL=redis://:YOUR_PASS@redis:6379
183
204
- JWT_SECRET=YOUR_SECRET
186
207
- CRON_SECRET=YOUR_SECRET
187
208
- RUNNER_SECRET=YOUR_SECRET
188
209
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
210
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
189
211
- SITE_URL=https://git.yourddns.net
190
212
- STORAGE_TYPE=local
191
213
- STORAGE_PATH=/data/storage
214
+ - GIT_REPOS_PATH=/data/repos
215
+ - REPOS_PATH=/data/repos
216
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
217
+ - GIT_SSH_PORT=2222
218
+ volumes:
219
+ - /volume1/opencodehub/repos:/data/repos
220
+ - /volume1/opencodehub/storage:/data/storage
221
+ - /volume1/opencodehub/ssh:/data/ssh
222
+ - /volume1/opencodehub/cache:/data/cache
223
+ depends_on:
224
+ - postgres
225
+ - redis
226
+ networks:
227
+ - och-net
228
+
229
+ ssh-git:
230
+ build:
231
+ context: /volume1/docker/OpenCodeHub
232
+ dockerfile: Dockerfile
233
+ container_name: opencodehub-ssh
234
+ restart: unless-stopped
235
+ ports:
236
+ - "2222:2222"
237
+ environment:
238
+ - NODE_ENV=production
239
+ - PROCESS_TYPE=ssh
240
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
241
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
242
+ - JWT_SECRET=YOUR_SECRET
243
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
244
+ - GIT_REPOS_PATH=/data/repos
245
+ - REPOS_PATH=/data/repos
246
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
247
+ - GIT_SSH_PORT=2222
248
+ volumes:
249
+ - /volume1/opencodehub/repos:/data/repos
250
+ - /volume1/opencodehub/storage:/data/storage
251
+ - /volume1/opencodehub/ssh:/data/ssh
252
+ - /volume1/opencodehub/cache:/data/cache
253
+ depends_on:
254
+ - postgres
255
+ - redis
256
+ networks:
257
+ - och-net
258
+
259
+ worker:
260
+ build:
261
+ context: /volume1/docker/OpenCodeHub
262
+ dockerfile: Dockerfile
263
+ container_name: opencodehub-worker
264
+ restart: unless-stopped
265
+ environment:
266
+ - NODE_ENV=production
267
+ - PROCESS_TYPE=worker
268
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
269
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
270
+ - JWT_SECRET=YOUR_SECRET
271
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
272
+ - GIT_REPOS_PATH=/data/repos
192
273
- REPOS_PATH=/data/repos
193
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
274
+ - STORAGE_TYPE=local
275
+ - STORAGE_PATH=/data/storage
194
276
volumes:
195
277
- /volume1/opencodehub/repos:/data/repos
196
278
- /volume1/opencodehub/storage:/data/storage
301
383
302
384
services:
303
385
app:
304
- image: swadhinbiswas/opencodehub:latest
386
+ image: opencodehub/opencodehub:latest
305
387
container_name: opencodehub
306
388
restart: unless-stopped
307
389
ports:
308
390
- "4321:4321"
309
- - "2222:2222"
310
391
environment:
311
392
- NODE_ENV=production
393
+ - PROCESS_TYPE=app
312
394
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
313
395
- REDIS_URL=redis://:YOUR_PASS@redis:6379
314
396
- JWT_SECRET=YOUR_SECRET
317
399
- CRON_SECRET=YOUR_SECRET
318
400
- RUNNER_SECRET=YOUR_SECRET
319
401
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
402
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
320
403
- SITE_URL=https://git.yourdomain.com
321
404
- STORAGE_TYPE=local
322
405
- STORAGE_PATH=/data/storage
406
+ - GIT_REPOS_PATH=/data/repos
407
+ - REPOS_PATH=/data/repos
408
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
409
+ - GIT_SSH_PORT=2222
410
+ volumes:
411
+ - /mnt/YOURPOOL/opencodehub/repos:/data/repos
412
+ - /mnt/YOURPOOL/opencodehub/storage:/data/storage
413
+ - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh
414
+ - /mnt/YOURPOOL/opencodehub/cache:/data/cache
415
+ depends_on:
416
+ - postgres
417
+ - redis
418
+ networks:
419
+ - och-net
420
+
421
+ ssh-git:
422
+ image: opencodehub/opencodehub:latest
423
+ container_name: opencodehub-ssh
424
+ restart: unless-stopped
425
+ ports:
426
+ - "2222:2222"
427
+ environment:
428
+ - NODE_ENV=production
429
+ - PROCESS_TYPE=ssh
430
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
431
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
432
+ - JWT_SECRET=YOUR_SECRET
433
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
434
+ - GIT_REPOS_PATH=/data/repos
435
+ - REPOS_PATH=/data/repos
436
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
437
+ - GIT_SSH_PORT=2222
438
+ volumes:
439
+ - /mnt/YOURPOOL/opencodehub/repos:/data/repos
440
+ - /mnt/YOURPOOL/opencodehub/storage:/data/storage
441
+ - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh
442
+ - /mnt/YOURPOOL/opencodehub/cache:/data/cache
443
+ depends_on:
444
+ - postgres
445
+ - redis
446
+ networks:
447
+ - och-net
448
+
449
+ worker:
450
+ image: opencodehub/opencodehub:latest
451
+ container_name: opencodehub-worker
452
+ restart: unless-stopped
453
+ environment:
454
+ - NODE_ENV=production
455
+ - PROCESS_TYPE=worker
456
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
457
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
458
+ - JWT_SECRET=YOUR_SECRET
459
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
460
+ - GIT_REPOS_PATH=/data/repos
323
461
- REPOS_PATH=/data/repos
324
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
462
+ - STORAGE_TYPE=local
463
+ - STORAGE_PATH=/data/storage
325
464
volumes:
326
465
- /mnt/YOURPOOL/opencodehub/repos:/data/repos
327
466
- /mnt/YOURPOOL/opencodehub/storage:/data/storage
443
582
444
583
#### Create OpenCodeHub:
445
584
446
-1. Search and install `swadhinbiswas/opencodehub:latest`
585
+1. Search and install `opencodehub/opencodehub:latest`
447
586
2. Name: `opencodehub`
448
587
3. Network: `opencodehub-network`
449
588
4. Environment variables (same as Synology example)
496
635
497
636
services:
498
637
app:
499
- image: swadhinbiswas/opencodehub:latest
638
+ image: opencodehub/opencodehub:latest
500
639
container_name: opencodehub
501
640
restart: unless-stopped
502
641
ports:
503
642
- "4321:4321"
504
- - "2222:2222"
505
643
environment:
506
644
- NODE_ENV=production
645
+ - PROCESS_TYPE=app
507
646
- DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
508
647
- REDIS_URL=redis://:YOUR_PASS@redis:6379
509
648
- JWT_SECRET=YOUR_SECRET
512
651
- CRON_SECRET=YOUR_SECRET
513
652
- RUNNER_SECRET=YOUR_SECRET
514
653
- WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET
654
+ - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET
515
655
- SITE_URL=https://git.yourdomain.com
516
656
- STORAGE_TYPE=local
517
657
- STORAGE_PATH=/data/storage
658
+ - GIT_REPOS_PATH=/data/repos
659
+ - REPOS_PATH=/data/repos
660
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
661
+ - GIT_SSH_PORT=2222
662
+ volumes:
663
+ - ./repos:/data/repos
664
+ - ./storage:/data/storage
665
+ - ./ssh:/data/ssh
666
+ - ./cache:/data/cache
667
+ depends_on:
668
+ - postgres
669
+ - redis
670
+ networks:
671
+ - och-net
672
+
673
+ ssh-git:
674
+ image: opencodehub/opencodehub:latest
675
+ container_name: opencodehub-ssh
676
+ restart: unless-stopped
677
+ ports:
678
+ - "2222:2222"
679
+ environment:
680
+ - NODE_ENV=production
681
+ - PROCESS_TYPE=ssh
682
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
683
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
684
+ - JWT_SECRET=YOUR_SECRET
685
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
686
+ - GIT_REPOS_PATH=/data/repos
687
+ - REPOS_PATH=/data/repos
688
+ - GIT_SSH_HOST_KEY=/data/ssh/host_key
689
+ - GIT_SSH_PORT=2222
690
+ volumes:
691
+ - ./repos:/data/repos
692
+ - ./storage:/data/storage
693
+ - ./ssh:/data/ssh
694
+ - ./cache:/data/cache
695
+ depends_on:
696
+ - postgres
697
+ - redis
698
+ networks:
699
+ - och-net
700
+
701
+ worker:
702
+ image: opencodehub/opencodehub:latest
703
+ container_name: opencodehub-worker
704
+ restart: unless-stopped
705
+ environment:
706
+ - NODE_ENV=production
707
+ - PROCESS_TYPE=worker
708
+ - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub
709
+ - REDIS_URL=redis://:YOUR_PASS@redis:6379
710
+ - JWT_SECRET=YOUR_SECRET
711
+ - INTERNAL_HOOK_SECRET=YOUR_SECRET
712
+ - GIT_REPOS_PATH=/data/repos
518
713
- REPOS_PATH=/data/repos
519
- - SSH_HOST_KEY_PATH=/data/ssh/host_key
714
+ - STORAGE_TYPE=local
715
+ - STORAGE_PATH=/data/storage
520
716
volumes:
521
717
- ./repos:/data/repos
522
718
- ./storage:/data/storage
523
719
▼docs/administration/deployment.md
+40-36
116
116
117
117
**Optional but Recommended:**
118
118
```bash
119
-# Error Monitoring
120
-SENTRY_DSN=https://your-sentry-dsn
121
-
122
119
# Email
123
120
SMTP_HOST=smtp.example.com
124
121
SMTP_PORT=587
127
124
128
125
129
126
# Redis (for session management)
130
-REDIS_URL=redis://localhost:6379
127
+REDIS_URL=redis://:your-password@localhost:6379
131
128
```
132
129
133
130
### Deployment Steps
138
135
cd OpenCodeHub
139
136
140
137
# 2. Install dependencies
141
-npm install # or bun install
138
+bun install # or npm install
142
139
143
140
# 3. Create production .env
144
141
cp .env.example .env
145
-nano .env # Configure all production values
142
+nano .env # Configure all production values (secrets: openssl rand -hex 32)
146
143
147
144
# 4. Validate environment
148
145
bun run src/lib/env-validation.ts
149
146
# Should output: ✅ Environment validation passed
150
147
151
-# 5. Run database migrations
152
-npm run db:push
153
-# or generate versioned migrations:
154
-npm run db:generate
155
-npm run db:migrate
148
+# 5. Run database migrations (applies committed drizzle/ migrations)
149
+bun run migrate
150
+# or for dev: npm run db:push
156
151
157
152
# 6. Create admin user
158
153
bun run scripts/seed-admin.ts
161
156
# 7. Build for production
162
157
npm run build
163
158
164
-# 8. Start production server
165
-npm start
159
+# 8. Start the production server (built in step 7)
160
+npm start # → node ./dist/server/entry.mjs
166
161
```
167
162
163
+> **Docker** is the recommended path for production. Use the repository's
164
+> `docker-compose.yml` (quickstart) or `docker-compose.production.yml`
165
+> (Caddy TLS + isolated networks) — do not hand-roll a compose file.
166
+
168
167
### Docker Deployment (Recommended)
169
168
170
-```yaml
171
-# docker-compose.prod.yml
172
-version: '3.8'
169
+Use the repository's own compose files — they are kept in sync with the
170
+codebase and are production-safe (no default secrets, migrations applied
171
+at boot, worker + SSH services included):
172
+
173
+```bash
174
+# Quickstart (app + SSH + worker + PostgreSQL + Redis)
175
+cp .env.example .env # then set ALL secrets
176
+docker compose up -d
177
+
178
+# Production profile (adds Caddy TLS termination + isolated networks)
179
+docker compose -f docker-compose.production.yml --profile production up -d
180
+
181
+# NAS / dedicated-disk deployments
182
+OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d
183
+```
184
+
185
+Minimal reference compose (for illustration only):
173
186
187
+```yaml
174
188
services:
175
189
app:
176
190
build: .
177
191
restart: always
178
192
ports:
179
- - "3000:3000"
180
- env_file: .env.production
193
+ - "4321:4321"
194
+ env_file: .env
195
+ environment:
196
+ NODE_ENV: production
181
197
depends_on:
182
198
- postgres
183
199
- redis
184
- volumes:
185
- - ./data:/app/data
186
- environment:
187
- NODE_ENV: production
188
- healthcheck:
189
- test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
190
- interval: 30s
191
- timeout: 10s
192
- retries: 3
193
200
194
201
postgres:
195
- image: postgres:15-alpine
202
+ image: postgres:16-alpine
196
203
restart: always
197
204
environment:
198
205
POSTGRES_DB: opencodehub
199
206
POSTGRES_USER: opencodehub
200
207
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
201
- volumes:
202
- - postgres_data:/var/lib/postgresql/data
203
- ports:
204
- - "5432:5432"
205
208
206
209
redis:
207
210
image: redis:7-alpine
208
211
restart: always
209
212
command: redis-server --requirepass ${REDIS_PASSWORD}
210
- ports:
211
- - "6379:6379"
212
213
213
214
volumes:
214
215
postgres_data:
215
216
```
216
217
218
+> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git).
219
+> PostgreSQL (5432) and Redis (6379) stay on an internal network.
220
+
217
221
```bash
218
222
# Deploy with Docker
219
223
docker-compose -f docker-compose.prod.yml up -d
244
248
245
249
# Proxy configuration
246
250
location / {
247
- proxy_pass http://localhost:3000;
251
+ proxy_pass http://localhost:4321;
248
252
proxy_set_header Host $host;
249
253
proxy_set_header X-Real-IP $remote_addr;
250
254
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
258
262
259
263
# Git operations (larger timeouts)
260
264
location ~ ^/[^/]+/[^/]+\.git/ {
261
- proxy_pass http://localhost:3000;
265
+ proxy_pass http://localhost:4321;
262
266
proxy_read_timeout 600s;
263
267
proxy_send_timeout 600s;
264
268
proxy_buffer_size 128k;
265
269
▼docs/administration/expose-nas.md
+9-9
43
43
44
44
Whatever exposure method you pick, you need:
45
45
46
-1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`).
46
+1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`).
47
47
2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel.
48
48
3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service.
49
49
4. **Trusted Proxies** correctly set in OpenCodeHub (so the real client IP is preserved and rate limits are not bypassed). See `TRUSTED_PROXIES` in `.env.example`.
105
105
# in the Tailscale admin console: https://login.tailscale.com/admin
106
106
# (turn on the "Funnel" feature flag).
107
107
#
108
-# Then on the NAS, expose the port OpenCodeHub listens on (default 3000):
109
-sudo tailscale funnel 3000 on
108
+# Then on the NAS, expose the port OpenCodeHub listens on (default 4321):
109
+sudo tailscale funnel 4321 on
110
110
111
111
# Or expose a specific path with HTTPS-only:
112
-sudo tailscale funnel --https=443 http://localhost:3000
112
+sudo tailscale funnel --https=443 http://localhost:4321
113
113
```
114
114
115
115
You will get a public URL like `https://nas-hostname.tail-net.ts.net`.
137
137
run:
138
138
139
139
```bash
140
-sudo tailscale funnel 3000 on
140
+sudo tailscale funnel 4321 on
141
141
```
142
142
143
143
For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command
267
267
|---|---|
268
268
| Subdomain | `git` (or anything) |
269
269
| Domain | `example.com` |
270
-| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) |
270
+| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) |
271
271
272
272
If you used Option A (Docker bridge network), `opencodehub` must be the
273
273
service name in your OpenCodeHub `docker-compose.yml`. If you used
274
-host networking, use `localhost:3000`.
274
+host networking, use `localhost:4321`.
275
275
276
276
Save. After a few seconds, `https://git.example.com` should reach your
277
277
OpenCodeHub instance.
383
383
- Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust
384
384
dashboard.
385
385
- Confirm OpenCodeHub is reachable from the tunnel's network namespace:
386
- - Tailscale (host networking): `curl http://localhost:3000`
387
- - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/`
386
+ - Tailscale (host networking): `curl http://localhost:4321`
387
+ - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/`
388
388
389
389
### Real client IP is wrong
390
390
391
391
▼docs/administration/horizontal-scaling.md
+17-0
1
+# Horizontal Scaling
2
+
3
+OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads.
4
+
5
+## Stateless Application Servers
6
+The main Astro application and REST API endpoints are mostly stateless. You can run multiple instances of the app behind a load balancer (e.g., Nginx, HAProxy, or a cloud LB).
7
+
8
+## State Management
9
+- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS).
10
+- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers.
11
+- **Background Workers**: You can spin up multiple worker processes (`npm run worker:start`) across different nodes. They will pull jobs from the shared Redis queue.
12
+
13
+## Shared Git Storage
14
+To allow multiple app instances to access Git repositories over SSH and HTTP, the `GIT_REPOS_PATH` must point to a shared network file system (e.g., NFS, EFS) that supports POSIX file locking.
15
+
16
+## Pluggable Blob Storage
17
+Configure `STORAGE_TYPE=s3` and use an S3-compatible backend (MinIO, AWS S3, Cloudflare R2) so all nodes share access to the same artifact/LFS storage.
0
18
▼docs/administration/incident-runbook.md
+1-1
48
48
kubectl set env deployment/opencodehub FEATURE_X_DISABLED=true
49
49
50
50
# Option B: Rollback (Docker)
51
-docker run --rm swadhinbiswas/opencodehub:previous-tag
51
+docker run --rm opencodehub/opencodehub:previous-tag
52
52
# Update docker-compose.yml with previous tag
53
53
docker compose -f docker-compose.production.yml up -d
54
54
55
55
▼docs/administration/known-accepted-vulns.json
+15-0
1
1
{
2
2
"_comment": "Vulnerabilities that are allowed past CI gates. Each entry MUST be reviewed and documented in docs/administration/security.md with a clear remediation plan and target date. Do NOT add new entries without justification.",
3
3
"accepted": [
4
+ "@astrojs/vercel",
5
+ "@opentelemetry/propagator-jaeger",
6
+ "@opentelemetry/sdk-node",
7
+ "@vercel/nft",
4
8
"astro",
9
+ "brace-expansion",
10
+ "cacache",
11
+ "gaxios",
12
+ "gcp-metadata",
13
+ "glob",
14
+ "make-fetch-happen",
15
+ "minimatch",
16
+ "node-gyp",
17
+ "nodegit",
5
18
"nodemailer",
19
+ "rimraf",
20
+ "sharp",
6
21
"undici",
7
22
"vite"
8
23
],
9
24
▼docs/administration/monitoring.md
+2-0
8
8
9
9
- `GET /api/health` for liveness/readiness
10
10
- `GET /api/metrics` for Prometheus scraping
11
+ (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the
12
+ scrape config; see `deploy/prometheus.yml`)
11
13
- Grafana dashboards for request latency, queue health, and storage/DB timing
12
14
- Alerting for p95 regressions and service degradation
13
15
14
16
▼docs/cli.md
+694-0
1
+# OpenCodeHub CLI (och)
2
+
3
+The OpenCodeHub CLI (`och`) provides a GitHub CLI-like experience for interacting with OpenCodeHub from the command line.
4
+
5
+## Installation
6
+
7
+```bash
8
+npm install -g opencodehub-cli
9
+```
10
+
11
+## Quick Start
12
+
13
+```bash
14
+# 1. Login to your server
15
+och auth login --url http://your-server:4321
16
+
17
+# 2. Create a repository
18
+och repo create my-project --description "My new project"
19
+
20
+# 3. Push your code
21
+och repo push --branch main
22
+```
23
+
24
+---
25
+
26
+## Authentication
27
+
28
+### `och auth login`
29
+Login to OpenCodeHub.
30
+
31
+```bash
32
+och auth login --url http://your-server:4321
33
+```
34
+
35
+**Options:**
36
+| Option | Description |
37
+|--------|-------------|
38
+| `-u, --url <url>` | OpenCodeHub server URL |
39
+| `-t, --token <token>` | Personal access token (non-interactive) |
40
+| `--with-token` | Authenticate using interactive token entry |
41
+| `--ca-file <path>` | Path to custom CA bundle (self-signed TLS) |
42
+| `--insecure` | Disable TLS certificate verification |
43
+
44
+**Examples:**
45
+```bash
46
+# Interactive login
47
+och auth login --url http://localhost:4321
48
+
49
+# Non-interactive with token
50
+och auth login --url http://localhost:4321 -t och_xxxxx
51
+
52
+# Self-signed TLS
53
+och auth login --url https://git.example.com --ca-file /path/to/ca.pem
54
+```
55
+
56
+### `och auth logout`
57
+Logout from OpenCodeHub.
58
+
59
+```bash
60
+och auth logout
61
+```
62
+
63
+### `och auth status`
64
+Show current authentication status.
65
+
66
+```bash
67
+och auth status
68
+```
69
+
70
+---
71
+
72
+## Repository Management
73
+
74
+### `och repo create <name>`
75
+Create a new repository on OpenCodeHub.
76
+
77
+```bash
78
+och repo create my-project --description "A cool project"
79
+```
80
+
81
+**Options:**
82
+| Option | Description |
83
+|--------|-------------|
84
+| `-d, --description <desc>` | Repository description |
85
+| `-p, --private` | Make repository private |
86
+| `--no-init` | Don't initialize with README |
87
+
88
+### `och repo clone <repo>`
89
+Clone a repository from OpenCodeHub.
90
+
91
+```bash
92
+och repo clone owner/repo
93
+och repo clone owner/repo my-local-name
94
+```
95
+
96
+**Arguments:**
97
+| Argument | Description |
98
+|----------|-------------|
99
+| `<repo>` | Repository in `owner/name` format |
100
+| `[destination]` | Local directory name (optional) |
101
+
102
+### `och repo push`
103
+Push local repository to OpenCodeHub.
104
+
105
+```bash
106
+och repo push
107
+och repo push --branch main
108
+och repo push --force
109
+```
110
+
111
+**Options:**
112
+| Option | Description |
113
+|--------|-------------|
114
+| `-b, --branch <branch>` | Branch to push |
115
+| `-f, --force` | Force push (overwrite remote) |
116
+
117
+### `och repo list`
118
+List your repositories.
119
+
120
+```bash
121
+och repo list
122
+```
123
+
124
+---
125
+
126
+## Git Operations
127
+
128
+### `och git init`
129
+Initialize a repository for OpenCodeHub.
130
+
131
+```bash
132
+och git init
133
+och git init --url http://your-server:4321
134
+```
135
+
136
+**Options:**
137
+| Option | Description |
138
+|--------|-------------|
139
+| `-u, --url <url>` | OpenCodeHub server URL |
140
+
141
+### `och git push`
142
+Push to OpenCodeHub (shorthand for `repo push`).
143
+
144
+```bash
145
+och git push
146
+och git push --branch main --force
147
+```
148
+
149
+### `och git sync`
150
+Sync with remote (fetch + push).
151
+
152
+```bash
153
+och git sync
154
+och git sync --force
155
+```
156
+
157
+**Options:**
158
+| Option | Description |
159
+|--------|-------------|
160
+| `-f, --force` | Force push after sync |
161
+
162
+---
163
+
164
+## Pull Requests
165
+
166
+### `och pr create`
167
+Create a new pull request.
168
+
169
+```bash
170
+och pr create
171
+och pr create --title "feat: new feature" --base main
172
+```
173
+
174
+**Options:**
175
+| Option | Description |
176
+|--------|-------------|
177
+| `-t, --title <title>` | PR title |
178
+| `-b, --base <branch>` | Base branch |
179
+| `-h, --head <branch>` | Head branch |
180
+| `-d, --description <desc>` | PR description |
181
+
182
+### `och pr list`
183
+List pull requests.
184
+
185
+```bash
186
+och pr list
187
+och pr list --state open
188
+```
189
+
190
+**Options:**
191
+| Option | Description |
192
+|--------|-------------|
193
+| `-s, --state <state>` | Filter by state (open, closed, all) |
194
+
195
+### `och pr view <number>`
196
+View a pull request.
197
+
198
+```bash
199
+och pr view 42
200
+```
201
+
202
+### `och pr checkout <number>`
203
+Checkout a pull request locally.
204
+
205
+```bash
206
+och pr checkout 42
207
+```
208
+
209
+### `och pr merge <number>`
210
+Merge a pull request.
211
+
212
+```bash
213
+och pr merge 42
214
+```
215
+
216
+### `och pr close <number>`
217
+Close a pull request.
218
+
219
+```bash
220
+och pr close 42
221
+```
222
+
223
+---
224
+
225
+## Stacked PRs
226
+
227
+### `och stack create <name>`
228
+Create a new branch in the current stack.
229
+
230
+```bash
231
+och stack create feature/child-1
232
+```
233
+
234
+### `och stack submit`
235
+Push stack and create/update PRs for all branches.
236
+
237
+```bash
238
+och stack submit
239
+```
240
+
241
+### `och stack sync`
242
+Rebase stack on updated main.
243
+
244
+```bash
245
+och stack sync
246
+```
247
+
248
+### `och stack log`
249
+Visualize stack tree.
250
+
251
+```bash
252
+och stack log
253
+```
254
+
255
+### `och stack status`
256
+Show current stack and branch status.
257
+
258
+```bash
259
+och stack status
260
+```
261
+
262
+---
263
+
264
+## Issues
265
+
266
+### `och issue create`
267
+Create a new issue.
268
+
269
+```bash
270
+och issue create --title "Bug: something broken" --body "Description..."
271
+```
272
+
273
+**Options:**
274
+| Option | Description |
275
+|--------|-------------|
276
+| `-t, --title <title>` | Issue title |
277
+| `-b, --body <body>` | Issue body |
278
+| `--labels <labels>` | Comma-separated labels |
279
+
280
+### `och issue list`
281
+List issues.
282
+
283
+```bash
284
+och issue list
285
+och issue list --state open
286
+```
287
+
288
+**Options:**
289
+| Option | Description |
290
+|--------|-------------|
291
+| `-s, --state <state>` | Filter by state (open, closed, all) |
292
+
293
+### `och issue view <number>`
294
+View an issue.
295
+
296
+```bash
297
+och issue view 42
298
+```
299
+
300
+### `och issue close <number>`
301
+Close an issue.
302
+
303
+```bash
304
+och issue close 42
305
+```
306
+
307
+---
308
+
309
+## Branches
310
+
311
+### `och branch checkout [branch]`
312
+Checkout a branch (interactive if no branch specified).
313
+
314
+```bash
315
+och branch checkout
316
+och branch checkout main
317
+```
318
+
319
+### `och branch rename [old-name] <new-name>`
320
+Rename a branch.
321
+
322
+```bash
323
+och branch rename old-name new-name
324
+```
325
+
326
+---
327
+
328
+## Merge Queue
329
+
330
+### `och queue add <pr-number>`
331
+Add a pull request to the merge queue.
332
+
333
+```bash
334
+och queue add 42
335
+```
336
+
337
+### `och queue list`
338
+List merge queue items.
339
+
340
+```bash
341
+och queue list
342
+```
343
+
344
+### `och queue remove <pr-number>`
345
+Remove a pull request from the merge queue.
346
+
347
+```bash
348
+och queue remove 42
349
+```
350
+
351
+---
352
+
353
+## CI/CD
354
+
355
+### `och ci list`
356
+List pipeline runs.
357
+
358
+```bash
359
+och ci list
360
+```
361
+
362
+### `och ci view <run-id>`
363
+View a pipeline run.
364
+
365
+```bash
366
+och ci view run-123
367
+```
368
+
369
+### `och ci cancel <run-id>`
370
+Cancel a pipeline run.
371
+
372
+```bash
373
+och ci cancel run-123
374
+```
375
+
376
+---
377
+
378
+## Releases
379
+
380
+### `och release create <tag>`
381
+Create a new release.
382
+
383
+```bash
384
+och release create v1.0.0 --name "Version 1.0.0" --body "Release notes..."
385
+```
386
+
387
+**Options:**
388
+| Option | Description |
389
+|--------|-------------|
390
+| `-n, --name <name>` | Release name |
391
+| `-b, --body <body>` | Release notes |
392
+| `--draft` | Create as draft |
393
+| `--prerelease` | Mark as pre-release |
394
+
395
+### `och release list`
396
+List releases.
397
+
398
+```bash
399
+och release list
400
+```
401
+
402
+---
403
+
404
+## Code Review
405
+
406
+### `och review ai <pr-number>`
407
+Trigger AI code review for a pull request.
408
+
409
+```bash
410
+och review ai 42
411
+```
412
+
413
+### `och review status <pr-number>`
414
+Check AI review status.
415
+
416
+```bash
417
+och review status 42
418
+```
419
+
420
+---
421
+
422
+## Secrets
423
+
424
+### `och secret set <name>`
425
+Set a secret.
426
+
427
+```bash
428
+och secret set MY_SECRET
429
+```
430
+
431
+### `och secret list`
432
+List secrets.
433
+
434
+```bash
435
+och secret list
436
+```
437
+
438
+### `och secret delete <name>`
439
+Delete a secret.
440
+
441
+```bash
442
+och secret delete MY_SECRET
443
+```
444
+
445
+---
446
+
447
+## Notifications
448
+
449
+### `och notify list`
450
+List your notifications.
451
+
452
+```bash
453
+och notify list
454
+```
455
+
456
+### `och notify read [id]`
457
+Mark notification(s) as read.
458
+
459
+```bash
460
+och notify read
461
+och notify read notification-id
462
+```
463
+
464
+---
465
+
466
+## SSH Keys
467
+
468
+### `och ssh-key add`
469
+Add an SSH key to your account.
470
+
471
+```bash
472
+och ssh-key add
473
+```
474
+
475
+### `och ssh-key list`
476
+List your SSH keys.
477
+
478
+```bash
479
+och ssh-key list
480
+```
481
+
482
+### `och ssh-key delete <id>`
483
+Delete an SSH key.
484
+
485
+```bash
486
+och ssh-key delete key-id
487
+```
488
+
489
+---
490
+
491
+## Search
492
+
493
+### `och search repos <query>`
494
+Search repositories.
495
+
496
+```bash
497
+och search repos "machine learning"
498
+```
499
+
500
+### `och search issues <query>`
501
+Search issues.
502
+
503
+```bash
504
+och search issues "bug fix"
505
+```
506
+
507
+---
508
+
509
+## Insights
510
+
511
+### `och insights show`
512
+Show your developer metrics.
513
+
514
+```bash
515
+och insights show
516
+```
517
+
518
+### `och insights team`
519
+Show team/repository metrics.
520
+
521
+```bash
522
+och insights team
523
+```
524
+
525
+---
526
+
527
+## Configuration
528
+
529
+### `och config list`
530
+List all configuration values.
531
+
532
+```bash
533
+och config list
534
+```
535
+
536
+### `och config get <key>`
537
+Get a configuration value.
538
+
539
+```bash
540
+och config get serverUrl
541
+```
542
+
543
+### `och config set <key> <value>`
544
+Set a configuration value.
545
+
546
+```bash
547
+och config set serverUrl http://localhost:4321
548
+```
549
+
550
+---
551
+
552
+## Automations
553
+
554
+### `och automate list`
555
+List automation rules for current repository.
556
+
557
+```bash
558
+och automate list
559
+```
560
+
561
+### `och automate create`
562
+Create a new automation rule.
563
+
564
+```bash
565
+och automate create
566
+```
567
+
568
+---
569
+
570
+## Runner
571
+
572
+### `och runner config`
573
+Configure a self-hosted runner interactively.
574
+
575
+```bash
576
+och runner config
577
+```
578
+
579
+---
580
+
581
+## API
582
+
583
+### `och api <endpoint>`
584
+Make direct API requests.
585
+
586
+```bash
587
+och api /api/user
588
+och api /api/repos --method POST --data '{"name": "my-repo"}'
589
+```
590
+
591
+**Options:**
592
+| Option | Description |
593
+|--------|-------------|
594
+| `-m, --method <method>` | HTTP method (GET, POST, PUT, DELETE) |
595
+| `-d, --data <data>` | Request body (JSON) |
596
+
597
+---
598
+
599
+## Focus Mode
600
+
601
+### `och focus`
602
+Interactive stack and review cockpit.
603
+
604
+```bash
605
+och focus
606
+```
607
+
608
+A terminal UI for managing PRs, reviews, and merge queue.
609
+
610
+---
611
+
612
+## Shell Completions
613
+
614
+### `och completion`
615
+Generate shell completions.
616
+
617
+```bash
618
+# Bash
619
+och completion > ~/.bashrc.d/och-completion.bash
620
+
621
+# Zsh
622
+och completion > ~/.zsh/completions/_och
623
+
624
+# Fish
625
+och completion --fish > ~/.config/fish/completions/och.fish
626
+```
627
+
628
+---
629
+
630
+## Environment Variables
631
+
632
+| Variable | Description |
633
+|----------|-------------|
634
+| `OCH_TOKEN` | Personal access token (overrides config) |
635
+| `OCH_SERVER_URL` | Server URL (overrides config) |
636
+| `OCH_HTTP_TIMEOUT_MS` | HTTP request timeout (default: 15000) |
637
+
638
+---
639
+
640
+## Configuration File
641
+
642
+The CLI stores configuration in `~/.config/opencodehub-cli/config.json`:
643
+
644
+```json
645
+{
646
+ "serverUrl": "http://localhost:4321",
647
+ "token": "och_xxxxx",
648
+ "username": "your-username"
649
+}
650
+```
651
+
652
+---
653
+
654
+## Common Workflows
655
+
656
+### New Repository
657
+```bash
658
+och auth login --url http://your-server:4321
659
+mkdir my-project && cd my-project
660
+git init
661
+och repo create my-project --description "My project"
662
+# Add files and commit
663
+och repo push --branch main
664
+```
665
+
666
+### Daily Development
667
+```bash
668
+och git sync # Pull latest changes
669
+och branch checkout feature-x # Switch branch
670
+# Make changes and commit
671
+och git push # Push changes
672
+och pr create --title "feat: X" # Create PR
673
+```
674
+
675
+### Stacked PRs
676
+```bash
677
+och stack create feature/part-1 # Create first branch
678
+# Work on part 1
679
+och stack submit # Push & create PR
680
+
681
+och stack create feature/part-2 # Create child branch
682
+# Work on part 2
683
+och stack submit # Push & create PR
684
+
685
+och stack log # View stack hierarchy
686
+```
687
+
688
+### Code Review
689
+```bash
690
+och pr list # See open PRs
691
+och pr checkout 42 # Checkout PR branch
692
+# Review code
693
+och pr merge 42 # Merge when ready
694
+```
0
695
▼docs/development/contributing.md
+1-1
49
49
bun dev
50
50
```
51
51
52
-Visit `http://localhost:3000`.
52
+Visit `http://localhost:4321`.
53
53
54
54
---
55
55
56
56
▼docs/development/release-gates.md
+21-0
1
+# Release Gates
2
+
3
+This document outlines the required gates that must be passed before any code can be merged into the default branch.
4
+
5
+## 1. Continuous Integration (CI)
6
+All pull requests must pass the following CI checks:
7
+- **Linting**: `npm run lint` must exit with 0.
8
+- **Typechecking**: `npm run typecheck` must exit with 0.
9
+- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass.
10
+
11
+## 2. Security Scans
12
+- **Secret Scanning**: Commits must be scanned for leaked secrets.
13
+- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold.
14
+- **Authorization Regression**: Core RBAC and permissions tests must pass.
15
+
16
+## 3. Documentation Parity
17
+Any feature addition must include updates to both documentation stacks:
18
+- The Markdown docs in `docs/`
19
+- The Starlight site in `docs-site/src/content/docs/`
20
+
21
+Features will not be merged if their documentation is missing or incomplete.
0
22
▼docs/getting-started/installation.md
+2-2
47
47
48
48
# Domain Configuration
49
49
SITE_URL=https://git.yourcompany.com
50
-PORT=3000
50
+PORT=4321
51
51
52
52
# Database (Using the Postgres container defined in compose)
53
53
DATABASE_URL=postgresql://opencodehub:securepassword@postgres:5432/opencodehub
98
98
client_max_body_size 500M;
99
99
100
100
location / {
101
- proxy_pass http://localhost:3000;
101
+ proxy_pass http://localhost:4321;
102
102
proxy_set_header Host $host;
103
103
proxy_set_header X-Real-IP $remote_addr;
104
104
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
105
105
▼docs/getting-started/quick-start.md
+1-1
3
3
Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes.
4
4
5
5
## 1. Login
6
-Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation.
6
+Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation.
7
7
8
8
![Login Screen](../../assets/login-screen.png)
9
9
10
10
▼docs/getting-started/ssh-keys.md
+120-0
1
+---
2
+title: "SSH Keys & Deploy Keys"
3
+description: "How to set up SSH keys for secure Git operations and deploy keys for CI/CD"
4
+---
5
+
6
+# SSH Keys & Deploy Keys
7
+
8
+OpenCodeHub supports SSH key authentication for secure Git operations without passwords.
9
+
10
+## SSH Keys (Account-Level)
11
+
12
+SSH keys give you access to **all your repositories**. Use them for daily development.
13
+
14
+### Generating an SSH Key
15
+
16
+**Ed25519 (recommended):**
17
+```bash
18
+ssh-keygen -t ed25519 -C "[email protected]"
19
+```
20
+
21
+**RSA (legacy):**
22
+```bash
23
+ssh-keygen -t rsa -b 4096 -C "[email protected]"
24
+```
25
+
26
+### Adding Your Key
27
+
28
+1. Copy your public key:
29
+ ```bash
30
+ cat ~/.ssh/id_ed25519.pub
31
+ ```
32
+
33
+2. Go to **Settings → SSH Keys** in OpenCodeHub
34
+
35
+3. Click **New SSH Key**
36
+
37
+4. Paste your public key and give it a title (e.g., "Work Laptop")
38
+
39
+### Testing Your Connection
40
+
41
+```bash
42
43
+```
44
+
45
+You should see: `Hi username! You've successfully authenticated`
46
+
47
+> **Note:** Replace `your-server.com` with your actual OpenCodeHub server hostname or IP address.
48
+
49
+### Using SSH with Git
50
+
51
+```bash
52
+# Clone via SSH
53
+git clone [email protected]:owner/repo.git
54
+
55
+# Add SSH remote to existing repo
56
+git remote add origin [email protected]:owner/repo.git
57
+```
58
+
59
+## Deploy Keys (Repository-Level)
60
+
61
+Deploy keys grant access to a **single repository**. Use them for CI/CD and deployments.
62
+
63
+### When to Use Deploy Keys
64
+
65
+- CI/CD pipelines that need to pull code
66
+- Deployment servers
67
+- Automation scripts with limited access
68
+
69
+### Generating a Deploy Key
70
+
71
+```bash
72
+ssh-keygen -t ed25519 -C "deploy@production-server"
73
+```
74
+
75
+### Adding a Deploy Key
76
+
77
+1. Go to **Repository → Settings → Deploy Keys**
78
+
79
+2. Click **Add deploy key**
80
+
81
+3. Paste the public key and give it a title
82
+
83
+4. Choose **Read-only** or **Read/write** access
84
+
85
+### Key Differences
86
+
87
+| Feature | SSH Keys | Deploy Keys |
88
+|---------|----------|-------------|
89
+| Scope | All repositories | Single repository |
90
+| Access | Full account access | Read-only or read/write |
91
+| Use case | Daily development | CI/CD, deployment |
92
+
93
+## Troubleshooting
94
+
95
+### Permission Denied
96
+
97
+```bash
98
+# Check SSH agent
99
+ssh-add -l
100
+
101
+# Add key to agent
102
+ssh-add ~/.ssh/id_ed25519
103
+
104
+# Debug SSH connection
105
106
+```
107
+
108
+### Wrong Key Being Used
109
+
110
+```bash
111
+# Specify key explicitly
112
+ssh -i ~/.ssh/id_ed25519 [email protected]
113
+```
114
+
115
+### Host Key Verification Failed
116
+
117
+```bash
118
+# Add host to known_hosts
119
+ssh-keyscan your-server.com >> ~/.ssh/known_hosts
120
+```
0
121
▼docs/reference/api-versioning.md
+14-0
1
+# API Versioning
2
+
3
+OpenCodeHub provides a stable REST API and GraphQL endpoint.
4
+
5
+## Versioning Strategy
6
+Currently, the REST API does not have explicit `/v1/` prefixes in the route paths. Instead, the API follows a continuous evolution model where breaking changes are heavily minimized.
7
+
8
+When breaking changes are unavoidable:
9
+- They will be announced in the release notes.
10
+- A deprecation header (`Deprecation: <date>`) will be added to the response of the old endpoint.
11
+- Clients will have at least one minor release cycle to migrate.
12
+
13
+## GraphQL
14
+The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes.
0
15
▼drizzle/0000_abandoned_king_bedlam.sql
+0-36
1
-ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "ai_config" text;
2
-
3
-CREATE TABLE IF NOT EXISTS "organizations" (
4
- "id" text PRIMARY KEY NOT NULL,
5
- "name" text NOT NULL,
6
- "display_name" text,
7
- "description" text,
8
- "email" text,
9
- "location" text,
10
- "website" text,
11
- "avatar_url" text,
12
- "is_verified" boolean DEFAULT false,
13
- "created_at" timestamp DEFAULT now() NOT NULL,
14
- "updated_at" timestamp DEFAULT now() NOT NULL,
15
- CONSTRAINT "organizations_name_unique" UNIQUE("name")
16
-);
17
-
18
-CREATE TABLE IF NOT EXISTS "organization_members" (
19
- "organization_id" text NOT NULL,
20
- "user_id" text NOT NULL,
21
- "role" text DEFAULT 'member' NOT NULL,
22
- "created_at" timestamp DEFAULT now() NOT NULL,
23
- CONSTRAINT "organization_members_organization_id_user_id_pk" PRIMARY KEY("organization_id","user_id")
24
-);
25
-
26
-DO $$ BEGIN
27
- ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;
28
-EXCEPTION
29
- WHEN duplicate_object THEN null;
30
-END $$;
31
-
32
-DO $$ BEGIN
33
- ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
34
-EXCEPTION
35
- WHEN duplicate_object THEN null;
36
-END $$;
37
0
▼drizzle/0000_black_monster_badoon.sql
+0-992
1
-CREATE TABLE `email_verification_tokens` (
2
- `id` text PRIMARY KEY NOT NULL,
3
- `user_id` text NOT NULL,
4
- `token` text NOT NULL,
5
- `expires_at` text NOT NULL,
6
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
7
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
8
-);
9
-CREATE UNIQUE INDEX `email_verification_tokens_token_unique` ON `email_verification_tokens` (`token`);--> statement-breakpoint
10
-CREATE TABLE `gpg_keys` (
11
- `id` text PRIMARY KEY NOT NULL,
12
- `user_id` text NOT NULL,
13
- `name` text NOT NULL,
14
- `key_id` text NOT NULL,
15
- `public_key` text NOT NULL,
16
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
17
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
18
-);
19
-CREATE TABLE `oauth_accounts` (
20
- `id` text PRIMARY KEY NOT NULL,
21
- `user_id` text NOT NULL,
22
- `provider` text NOT NULL,
23
- `provider_account_id` text NOT NULL,
24
- `access_token` text,
25
- `refresh_token` text,
26
- `expires_at` text,
27
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
28
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
29
-);
30
-CREATE TABLE `password_reset_tokens` (
31
- `id` text PRIMARY KEY NOT NULL,
32
- `user_id` text NOT NULL,
33
- `token` text NOT NULL,
34
- `expires_at` text NOT NULL,
35
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
36
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
37
-);
38
-CREATE UNIQUE INDEX `password_reset_tokens_token_unique` ON `password_reset_tokens` (`token`);--> statement-breakpoint
39
-CREATE TABLE `personal_access_tokens` (
40
- `id` text PRIMARY KEY NOT NULL,
41
- `user_id` text NOT NULL,
42
- `name` text NOT NULL,
43
- `token` text NOT NULL,
44
- `expires_at` text,
45
- `last_used_at` text,
46
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
47
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
48
-);
49
-CREATE UNIQUE INDEX `personal_access_tokens_token_unique` ON `personal_access_tokens` (`token`);--> statement-breakpoint
50
-CREATE TABLE `sessions` (
51
- `id` text PRIMARY KEY NOT NULL,
52
- `user_id` text NOT NULL,
53
- `token` text NOT NULL,
54
- `user_agent` text,
55
- `ip_address` text,
56
- `expires_at` text NOT NULL,
57
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
58
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
59
-);
60
-CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint
61
-CREATE TABLE `ssh_keys` (
62
- `id` text PRIMARY KEY NOT NULL,
63
- `user_id` text NOT NULL,
64
- `title` text NOT NULL,
65
- `fingerprint` text NOT NULL,
66
- `public_key` text NOT NULL,
67
- `key_type` text NOT NULL,
68
- `last_used_at` text,
69
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
70
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
71
-);
72
-CREATE UNIQUE INDEX `ssh_keys_fingerprint_unique` ON `ssh_keys` (`fingerprint`);--> statement-breakpoint
73
-CREATE TABLE `users` (
74
- `id` text PRIMARY KEY NOT NULL,
75
- `username` text NOT NULL,
76
- `email` text NOT NULL,
77
- `password_hash` text,
78
- `display_name` text,
79
- `bio` text,
80
- `avatar_url` text,
81
- `location` text,
82
- `website` text,
83
- `company` text,
84
- `is_admin` integer DEFAULT false,
85
- `is_active` integer DEFAULT true,
86
- `email_verified` integer DEFAULT false,
87
- `two_factor_enabled` integer DEFAULT false,
88
- `two_factor_secret` text,
89
- `last_login_at` text,
90
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
91
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL
92
-);
93
-CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint
94
-CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
95
-CREATE TABLE `branches` (
96
- `id` text PRIMARY KEY NOT NULL,
97
- `repository_id` text NOT NULL,
98
- `name` text NOT NULL,
99
- `commit_sha` text NOT NULL,
100
- `is_protected` integer DEFAULT false,
101
- `is_default` integer DEFAULT false,
102
- `protection_rules` text,
103
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
104
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
105
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
106
-);
107
-CREATE TABLE `commits` (
108
- `id` text PRIMARY KEY NOT NULL,
109
- `repository_id` text NOT NULL,
110
- `sha` text NOT NULL,
111
- `message` text NOT NULL,
112
- `author_name` text NOT NULL,
113
- `author_email` text NOT NULL,
114
- `author_date` text NOT NULL,
115
- `committer_name` text NOT NULL,
116
- `committer_email` text NOT NULL,
117
- `committer_date` text NOT NULL,
118
- `parent_shas` text,
119
- `tree_sha` text,
120
- `user_id` text,
121
- `stats` text,
122
- `signature` text,
123
- `is_verified` integer DEFAULT false,
124
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
125
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
126
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
127
-);
128
-CREATE TABLE `releases` (
129
- `id` text PRIMARY KEY NOT NULL,
130
- `repository_id` text NOT NULL,
131
- `tag_id` text,
132
- `name` text NOT NULL,
133
- `body` text,
134
- `is_draft` integer DEFAULT false,
135
- `is_prerelease` integer DEFAULT false,
136
- `author_id` text,
137
- `assets` text,
138
- `download_count` integer DEFAULT 0,
139
- `published_at` text,
140
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
141
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
142
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
143
- FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE no action,
144
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
145
-);
146
-CREATE TABLE `repositories` (
147
- `id` text PRIMARY KEY NOT NULL,
148
- `name` text NOT NULL,
149
- `slug` text NOT NULL,
150
- `description` text,
151
- `owner_id` text NOT NULL,
152
- `owner_type` text DEFAULT 'user' NOT NULL,
153
- `visibility` text DEFAULT 'public' NOT NULL,
154
- `default_branch` text DEFAULT 'main' NOT NULL,
155
- `disk_path` text NOT NULL,
156
- `ssh_clone_url` text,
157
- `http_clone_url` text,
158
- `star_count` integer DEFAULT 0,
159
- `fork_count` integer DEFAULT 0,
160
- `watch_count` integer DEFAULT 0,
161
- `open_issue_count` integer DEFAULT 0,
162
- `open_pr_count` integer DEFAULT 0,
163
- `size` integer DEFAULT 0,
164
- `is_fork` integer DEFAULT false,
165
- `forked_from_id` text,
166
- `is_archived` integer DEFAULT false,
167
- `is_mirror` integer DEFAULT false,
168
- `mirror_url` text,
169
- `has_issues` integer DEFAULT true,
170
- `has_wiki` integer DEFAULT true,
171
- `has_actions` integer DEFAULT true,
172
- `allow_forking` integer DEFAULT true,
173
- `license_type` text,
174
- `topics` text,
175
- `language` text,
176
- `languages` text,
177
- `last_activity_at` text,
178
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
179
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
180
- FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
181
- FOREIGN KEY (`forked_from_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action
182
-);
183
-CREATE TABLE `repository_collaborators` (
184
- `id` text PRIMARY KEY NOT NULL,
185
- `repository_id` text NOT NULL,
186
- `user_id` text NOT NULL,
187
- `role` text DEFAULT 'developer' NOT NULL,
188
- `added_by_id` text,
189
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
190
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
191
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
192
- FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
193
-);
194
-CREATE TABLE `repository_stars` (
195
- `id` text PRIMARY KEY NOT NULL,
196
- `repository_id` text NOT NULL,
197
- `user_id` text NOT NULL,
198
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
199
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
200
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
201
-);
202
-CREATE TABLE `repository_watchers` (
203
- `id` text PRIMARY KEY NOT NULL,
204
- `repository_id` text NOT NULL,
205
- `user_id` text NOT NULL,
206
- `watch_level` text DEFAULT 'watching' NOT NULL,
207
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
208
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
209
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
210
-);
211
-CREATE TABLE `tags` (
212
- `id` text PRIMARY KEY NOT NULL,
213
- `repository_id` text NOT NULL,
214
- `name` text NOT NULL,
215
- `commit_sha` text NOT NULL,
216
- `message` text,
217
- `tagger_name` text,
218
- `tagger_email` text,
219
- `tagged_at` text,
220
- `is_release` integer DEFAULT false,
221
- `release_id` text,
222
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
223
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
224
-);
225
-CREATE TABLE `webhooks` (
226
- `id` text PRIMARY KEY NOT NULL,
227
- `repository_id` text NOT NULL,
228
- `url` text NOT NULL,
229
- `secret` text,
230
- `events` text NOT NULL,
231
- `is_active` integer DEFAULT true,
232
- `last_delivery_at` text,
233
- `last_delivery_status` integer,
234
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
235
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
236
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
237
-);
238
-CREATE TABLE `issue_assignees` (
239
- `id` text PRIMARY KEY NOT NULL,
240
- `issue_id` text NOT NULL,
241
- `user_id` text NOT NULL,
242
- `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
243
- FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade,
244
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
245
-);
246
-CREATE TABLE `issue_comments` (
247
- `id` text PRIMARY KEY NOT NULL,
248
- `issue_id` text NOT NULL,
249
- `author_id` text NOT NULL,
250
- `body` text NOT NULL,
251
- `reactions` text,
252
- `is_edited` integer DEFAULT false,
253
- `edited_at` text,
254
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
255
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
256
- FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade,
257
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
258
-);
259
-CREATE TABLE `issue_labels` (
260
- `id` text PRIMARY KEY NOT NULL,
261
- `issue_id` text NOT NULL,
262
- `label_id` text NOT NULL,
263
- FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade,
264
- FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade
265
-);
266
-CREATE TABLE `issue_subscribers` (
267
- `id` text PRIMARY KEY NOT NULL,
268
- `issue_id` text NOT NULL,
269
- `user_id` text NOT NULL,
270
- `subscribed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
271
- FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade,
272
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
273
-);
274
-CREATE TABLE `issues` (
275
- `id` text PRIMARY KEY NOT NULL,
276
- `repository_id` text NOT NULL,
277
- `number` integer NOT NULL,
278
- `title` text NOT NULL,
279
- `body` text,
280
- `state` text DEFAULT 'open' NOT NULL,
281
- `author_id` text NOT NULL,
282
- `assignee_id` text,
283
- `milestone_id` text,
284
- `is_pinned` integer DEFAULT false,
285
- `is_locked` integer DEFAULT false,
286
- `lock_reason` text,
287
- `comment_count` integer DEFAULT 0,
288
- `reactions` text,
289
- `closed_at` text,
290
- `closed_by_id` text,
291
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
292
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
293
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
294
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
295
- FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
296
- FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action,
297
- FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
298
-);
299
-CREATE TABLE `labels` (
300
- `id` text PRIMARY KEY NOT NULL,
301
- `repository_id` text NOT NULL,
302
- `name` text NOT NULL,
303
- `color` text DEFAULT '#6b7280' NOT NULL,
304
- `description` text,
305
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
306
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
307
-);
308
-CREATE TABLE `milestones` (
309
- `id` text PRIMARY KEY NOT NULL,
310
- `repository_id` text NOT NULL,
311
- `title` text NOT NULL,
312
- `description` text,
313
- `state` text DEFAULT 'open' NOT NULL,
314
- `due_date` text,
315
- `open_issue_count` integer DEFAULT 0,
316
- `closed_issue_count` integer DEFAULT 0,
317
- `closed_at` text,
318
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
319
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
320
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
321
-);
322
-CREATE TABLE `pull_request_assignees` (
323
- `id` text PRIMARY KEY NOT NULL,
324
- `pull_request_id` text NOT NULL,
325
- `user_id` text NOT NULL,
326
- `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
327
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
328
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
329
-);
330
-CREATE TABLE `pull_request_checks` (
331
- `id` text PRIMARY KEY NOT NULL,
332
- `pull_request_id` text NOT NULL,
333
- `name` text NOT NULL,
334
- `status` text NOT NULL,
335
- `conclusion` text,
336
- `head_sha` text NOT NULL,
337
- `external_id` text,
338
- `details_url` text,
339
- `output` text,
340
- `started_at` text,
341
- `completed_at` text,
342
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
343
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
344
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade
345
-);
346
-CREATE TABLE `pull_request_comments` (
347
- `id` text PRIMARY KEY NOT NULL,
348
- `pull_request_id` text NOT NULL,
349
- `review_id` text,
350
- `author_id` text NOT NULL,
351
- `body` text NOT NULL,
352
- `path` text,
353
- `line` integer,
354
- `side` text,
355
- `start_line` integer,
356
- `start_side` text,
357
- `commit_sha` text,
358
- `original_commit_sha` text,
359
- `original_line` integer,
360
- `in_reply_to_id` text,
361
- `reactions` text,
362
- `is_resolved` integer DEFAULT false,
363
- `resolved_by_id` text,
364
- `resolved_at` text,
365
- `is_edited` integer DEFAULT false,
366
- `edited_at` text,
367
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
368
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
369
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
370
- FOREIGN KEY (`review_id`) REFERENCES `pull_request_reviews`(`id`) ON UPDATE no action ON DELETE no action,
371
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
372
- FOREIGN KEY (`in_reply_to_id`) REFERENCES `pull_request_comments`(`id`) ON UPDATE no action ON DELETE no action,
373
- FOREIGN KEY (`resolved_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
374
-);
375
-CREATE TABLE `pull_request_labels` (
376
- `id` text PRIMARY KEY NOT NULL,
377
- `pull_request_id` text NOT NULL,
378
- `label_id` text NOT NULL,
379
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
380
- FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade
381
-);
382
-CREATE TABLE `pull_request_reviewers` (
383
- `id` text PRIMARY KEY NOT NULL,
384
- `pull_request_id` text NOT NULL,
385
- `user_id` text NOT NULL,
386
- `is_required` integer DEFAULT false,
387
- `requested_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
388
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
389
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
390
-);
391
-CREATE TABLE `pull_request_reviews` (
392
- `id` text PRIMARY KEY NOT NULL,
393
- `pull_request_id` text NOT NULL,
394
- `reviewer_id` text NOT NULL,
395
- `state` text NOT NULL,
396
- `body` text,
397
- `commit_sha` text,
398
- `submitted_at` text,
399
- `dismissed_at` text,
400
- `dismissed_by_id` text,
401
- `dismissal_reason` text,
402
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
403
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
404
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
405
- FOREIGN KEY (`reviewer_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
406
- FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
407
-);
408
-CREATE TABLE `pull_requests` (
409
- `id` text PRIMARY KEY NOT NULL,
410
- `repository_id` text NOT NULL,
411
- `number` integer NOT NULL,
412
- `title` text NOT NULL,
413
- `body` text,
414
- `state` text DEFAULT 'open' NOT NULL,
415
- `author_id` text NOT NULL,
416
- `assignee_id` text,
417
- `milestone_id` text,
418
- `head_branch` text NOT NULL,
419
- `head_sha` text NOT NULL,
420
- `head_repository_id` text,
421
- `base_branch` text NOT NULL,
422
- `base_sha` text NOT NULL,
423
- `is_draft` integer DEFAULT false,
424
- `is_merged` integer DEFAULT false,
425
- `merged_at` text,
426
- `merged_by_id` text,
427
- `merge_sha` text,
428
- `merge_method` text,
429
- `additions` integer DEFAULT 0,
430
- `deletions` integer DEFAULT 0,
431
- `changed_files` integer DEFAULT 0,
432
- `comment_count` integer DEFAULT 0,
433
- `review_count` integer DEFAULT 0,
434
- `mergeable` integer,
435
- `mergeable_state` text,
436
- `rebaseable` integer,
437
- `maintainer_can_modify` integer DEFAULT true,
438
- `allow_auto_merge` integer DEFAULT false,
439
- `auto_merge_method` text,
440
- `closed_at` text,
441
- `closed_by_id` text,
442
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
443
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
444
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
445
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
446
- FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
447
- FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action,
448
- FOREIGN KEY (`head_repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action,
449
- FOREIGN KEY (`merged_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
450
- FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
451
-);
452
-CREATE TABLE `scheduled_workflows` (
453
- `id` text PRIMARY KEY NOT NULL,
454
- `workflow_id` text NOT NULL,
455
- `cron_expression` text NOT NULL,
456
- `timezone` text DEFAULT 'UTC',
457
- `is_enabled` integer DEFAULT true,
458
- `last_run_at` text,
459
- `next_run_at` text,
460
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
461
- FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade
462
-);
463
-CREATE TABLE `workflow_artifacts` (
464
- `id` text PRIMARY KEY NOT NULL,
465
- `run_id` text NOT NULL,
466
- `job_id` text,
467
- `name` text NOT NULL,
468
- `size_bytes` integer NOT NULL,
469
- `storage_path` text NOT NULL,
470
- `mime_type` text,
471
- `download_count` integer DEFAULT 0,
472
- `expires_at` text,
473
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
474
- FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade,
475
- FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE no action
476
-);
477
-CREATE TABLE `workflow_jobs` (
478
- `id` text PRIMARY KEY NOT NULL,
479
- `run_id` text NOT NULL,
480
- `name` text NOT NULL,
481
- `status` text DEFAULT 'queued' NOT NULL,
482
- `conclusion` text,
483
- `runner_id` text,
484
- `runner_name` text,
485
- `runner_group_id` text,
486
- `runner_group_name` text,
487
- `container_id` text,
488
- `container_image` text,
489
- `needs` text,
490
- `environment` text,
491
- `environment_url` text,
492
- `matrix` text,
493
- `started_at` text,
494
- `completed_at` text,
495
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
496
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
497
- FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade
498
-);
499
-CREATE TABLE `workflow_logs` (
500
- `id` text PRIMARY KEY NOT NULL,
501
- `job_id` text NOT NULL,
502
- `step_id` text,
503
- `log_level` text DEFAULT 'info' NOT NULL,
504
- `message` text NOT NULL,
505
- `timestamp` text NOT NULL,
506
- `line_number` integer,
507
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
508
- FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade,
509
- FOREIGN KEY (`step_id`) REFERENCES `workflow_steps`(`id`) ON UPDATE no action ON DELETE no action
510
-);
511
-CREATE TABLE `workflow_runs` (
512
- `id` text PRIMARY KEY NOT NULL,
513
- `workflow_id` text NOT NULL,
514
- `repository_id` text NOT NULL,
515
- `run_number` integer NOT NULL,
516
- `run_attempt` integer DEFAULT 1 NOT NULL,
517
- `name` text NOT NULL,
518
- `display_title` text,
519
- `status` text DEFAULT 'queued' NOT NULL,
520
- `conclusion` text,
521
- `event` text NOT NULL,
522
- `head_branch` text,
523
- `head_sha` text NOT NULL,
524
- `base_branch` text,
525
- `base_sha` text,
526
- `pull_request_id` text,
527
- `triggered_by_id` text,
528
- `started_at` text,
529
- `completed_at` text,
530
- `workflow_config` text,
531
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
532
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
533
- FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade,
534
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
535
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action,
536
- FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
537
-);
538
-CREATE TABLE `workflow_secrets` (
539
- `id` text PRIMARY KEY NOT NULL,
540
- `repository_id` text NOT NULL,
541
- `name` text NOT NULL,
542
- `encrypted_value` text NOT NULL,
543
- `environment` text,
544
- `created_by_id` text,
545
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
546
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
547
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
548
- FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
549
-);
550
-CREATE TABLE `workflow_steps` (
551
- `id` text PRIMARY KEY NOT NULL,
552
- `job_id` text NOT NULL,
553
- `number` integer NOT NULL,
554
- `name` text NOT NULL,
555
- `status` text DEFAULT 'queued' NOT NULL,
556
- `conclusion` text,
557
- `uses` text,
558
- `run` text,
559
- `shell` text,
560
- `working_directory` text,
561
- `env` text,
562
- `with` text,
563
- `if` text,
564
- `continue_on_error` integer DEFAULT false,
565
- `timeout_minutes` integer,
566
- `started_at` text,
567
- `completed_at` text,
568
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
569
- FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade
570
-);
571
-CREATE TABLE `workflow_variables` (
572
- `id` text PRIMARY KEY NOT NULL,
573
- `repository_id` text NOT NULL,
574
- `name` text NOT NULL,
575
- `value` text NOT NULL,
576
- `environment` text,
577
- `created_by_id` text,
578
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
579
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
580
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
581
- FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
582
-);
583
-CREATE TABLE `workflows` (
584
- `id` text PRIMARY KEY NOT NULL,
585
- `repository_id` text NOT NULL,
586
- `name` text NOT NULL,
587
- `path` text NOT NULL,
588
- `state` text DEFAULT 'active' NOT NULL,
589
- `badge_url` text,
590
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
591
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
592
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
593
-);
594
-CREATE TABLE `wiki_pages` (
595
- `id` text PRIMARY KEY NOT NULL,
596
- `repository_id` text NOT NULL,
597
- `slug` text NOT NULL,
598
- `title` text NOT NULL,
599
- `content` text NOT NULL,
600
- `format` text DEFAULT 'markdown' NOT NULL,
601
- `parent_id` text,
602
- `order` integer DEFAULT 0,
603
- `last_editor_id` text,
604
- `view_count` integer DEFAULT 0,
605
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
606
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
607
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
608
- FOREIGN KEY (`parent_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE no action,
609
- FOREIGN KEY (`last_editor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
610
-);
611
-CREATE TABLE `wiki_revisions` (
612
- `id` text PRIMARY KEY NOT NULL,
613
- `page_id` text NOT NULL,
614
- `content` text NOT NULL,
615
- `message` text,
616
- `author_id` text NOT NULL,
617
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
618
- FOREIGN KEY (`page_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE cascade,
619
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
620
-);
621
-CREATE TABLE `organization_members` (
622
- `id` text PRIMARY KEY NOT NULL,
623
- `organization_id` text NOT NULL,
624
- `user_id` text NOT NULL,
625
- `role` text DEFAULT 'member' NOT NULL,
626
- `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
627
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade,
628
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
629
-);
630
-CREATE TABLE `organizations` (
631
- `id` text PRIMARY KEY NOT NULL,
632
- `name` text NOT NULL,
633
- `display_name` text,
634
- `description` text,
635
- `avatar_url` text,
636
- `website` text,
637
- `location` text,
638
- `email` text,
639
- `is_verified` integer DEFAULT false,
640
- `visibility` text DEFAULT 'public' NOT NULL,
641
- `default_repo_permission` text DEFAULT 'read',
642
- `members_can_create_repos` integer DEFAULT true,
643
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
644
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL
645
-);
646
-CREATE UNIQUE INDEX `organizations_name_unique` ON `organizations` (`name`);--> statement-breakpoint
647
-CREATE TABLE `team_members` (
648
- `id` text PRIMARY KEY NOT NULL,
649
- `team_id` text NOT NULL,
650
- `user_id` text NOT NULL,
651
- `role` text DEFAULT 'member' NOT NULL,
652
- `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
653
- FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade,
654
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
655
-);
656
-CREATE TABLE `team_repositories` (
657
- `id` text PRIMARY KEY NOT NULL,
658
- `team_id` text NOT NULL,
659
- `repository_id` text NOT NULL,
660
- `permission` text DEFAULT 'read' NOT NULL,
661
- `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
662
- FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade,
663
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
664
-);
665
-CREATE TABLE `teams` (
666
- `id` text PRIMARY KEY NOT NULL,
667
- `organization_id` text NOT NULL,
668
- `name` text NOT NULL,
669
- `slug` text NOT NULL,
670
- `description` text,
671
- `privacy` text DEFAULT 'visible' NOT NULL,
672
- `parent_id` text,
673
- `permission` text DEFAULT 'read' NOT NULL,
674
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
675
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
676
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade,
677
- FOREIGN KEY (`parent_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE no action
678
-);
679
-CREATE TABLE `activities` (
680
- `id` text PRIMARY KEY NOT NULL,
681
- `user_id` text NOT NULL,
682
- `repository_id` text,
683
- `organization_id` text,
684
- `type` text NOT NULL,
685
- `action` text NOT NULL,
686
- `ref_type` text,
687
- `ref_name` text,
688
- `target_type` text,
689
- `target_id` text,
690
- `payload` text,
691
- `is_public` integer DEFAULT true,
692
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
693
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
694
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
695
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action
696
-);
697
-CREATE TABLE `audit_logs` (
698
- `id` text PRIMARY KEY NOT NULL,
699
- `user_id` text,
700
- `organization_id` text,
701
- `repository_id` text,
702
- `action` text NOT NULL,
703
- `actor_type` text DEFAULT 'user' NOT NULL,
704
- `actor_id` text,
705
- `actor_ip` text,
706
- `actor_user_agent` text,
707
- `target_type` text,
708
- `target_id` text,
709
- `target_name` text,
710
- `data` text,
711
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
712
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
713
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action,
714
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action
715
-);
716
-CREATE TABLE `notifications` (
717
- `id` text PRIMARY KEY NOT NULL,
718
- `user_id` text NOT NULL,
719
- `repository_id` text,
720
- `type` text NOT NULL,
721
- `title` text NOT NULL,
722
- `body` text,
723
- `url` text,
724
- `actor_id` text,
725
- `subject_type` text,
726
- `subject_id` text,
727
- `reason` text NOT NULL,
728
- `is_read` integer DEFAULT false,
729
- `read_at` text,
730
- `is_archived` integer DEFAULT false,
731
- `archived_at` text,
732
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
733
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
734
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
735
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action,
736
- FOREIGN KEY (`actor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
737
-);
738
-CREATE TABLE `security_scans` (
739
- `id` text PRIMARY KEY NOT NULL,
740
- `repository_id` text NOT NULL,
741
- `status` text DEFAULT 'queued' NOT NULL,
742
- `started_at` integer,
743
- `completed_at` integer,
744
- `critical_count` integer DEFAULT 0,
745
- `high_count` integer DEFAULT 0,
746
- `medium_count` integer DEFAULT 0,
747
- `low_count` integer DEFAULT 0,
748
- `unknown_count` integer DEFAULT 0,
749
- `logs` text,
750
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
751
-);
752
-CREATE TABLE `security_vulnerabilities` (
753
- `id` text PRIMARY KEY NOT NULL,
754
- `scan_id` text NOT NULL,
755
- `vulnerability_id` text NOT NULL,
756
- `pkg_name` text NOT NULL,
757
- `installed_version` text,
758
- `fixed_version` text,
759
- `severity` text NOT NULL,
760
- `title` text,
761
- `description` text,
762
- `target` text,
763
- `class` text,
764
- FOREIGN KEY (`scan_id`) REFERENCES `security_scans`(`id`) ON UPDATE no action ON DELETE cascade
765
-);
766
-CREATE TABLE `pipeline_runners` (
767
- `id` text PRIMARY KEY NOT NULL,
768
- `repository_id` text,
769
- `token` text NOT NULL,
770
- `name` text NOT NULL,
771
- `os` text,
772
- `arch` text,
773
- `version` text,
774
- `status` text DEFAULT 'offline' NOT NULL,
775
- `last_seen_at` text,
776
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
777
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
778
-);
779
-CREATE TABLE `pr_stack_entries` (
780
- `id` text PRIMARY KEY NOT NULL,
781
- `stack_id` text NOT NULL,
782
- `pull_request_id` text NOT NULL,
783
- `stack_order` integer NOT NULL,
784
- `parent_pr_id` text,
785
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
786
- FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE cascade,
787
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
788
- FOREIGN KEY (`parent_pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action
789
-);
790
-CREATE TABLE `pr_stacks` (
791
- `id` text PRIMARY KEY NOT NULL,
792
- `repository_id` text NOT NULL,
793
- `name` text,
794
- `base_branch` text NOT NULL,
795
- `status` text DEFAULT 'active' NOT NULL,
796
- `created_by_id` text NOT NULL,
797
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
798
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
799
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
800
- FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
801
-);
802
-CREATE TABLE `merge_queue` (
803
- `id` text PRIMARY KEY NOT NULL,
804
- `repository_id` text NOT NULL,
805
- `pull_request_id` text NOT NULL,
806
- `stack_id` text,
807
- `status` text DEFAULT 'pending' NOT NULL,
808
- `priority` integer DEFAULT 0 NOT NULL,
809
- `position` integer,
810
- `ci_status` text DEFAULT 'pending',
811
- `ci_run_id` text,
812
- `added_by_id` text NOT NULL,
813
- `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
814
- `estimated_merge_at` text,
815
- `started_at` text,
816
- `completed_at` text,
817
- `failure_reason` text,
818
- `retry_count` integer DEFAULT 0,
819
- `merge_method` text DEFAULT 'merge',
820
- `delete_on_merge` integer DEFAULT true,
821
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
822
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
823
- FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE no action,
824
- FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
825
-);
826
-CREATE TABLE `ai_review_suggestions` (
827
- `id` text PRIMARY KEY NOT NULL,
828
- `ai_review_id` text NOT NULL,
829
- `path` text NOT NULL,
830
- `line` integer,
831
- `end_line` integer,
832
- `severity` text NOT NULL,
833
- `type` text NOT NULL,
834
- `category` text,
835
- `title` text NOT NULL,
836
- `message` text NOT NULL,
837
- `suggested_fix` text,
838
- `explanation` text,
839
- `is_applied` integer DEFAULT false,
840
- `is_dismissed` integer DEFAULT false,
841
- `applied_at` text,
842
- `applied_by_id` text,
843
- `dismissed_at` text,
844
- `dismissed_by_id` text,
845
- `dismiss_reason` text,
846
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
847
- FOREIGN KEY (`ai_review_id`) REFERENCES `ai_reviews`(`id`) ON UPDATE no action ON DELETE cascade,
848
- FOREIGN KEY (`applied_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
849
- FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
850
-);
851
-CREATE TABLE `ai_reviews` (
852
- `id` text PRIMARY KEY NOT NULL,
853
- `pull_request_id` text NOT NULL,
854
- `status` text DEFAULT 'pending' NOT NULL,
855
- `model` text NOT NULL,
856
- `provider` text NOT NULL,
857
- `stack_context` text,
858
- `includes_stack_context` integer DEFAULT false,
859
- `summary` text,
860
- `overall_severity` text,
861
- `suggestions_count` integer DEFAULT 0,
862
- `tokens_used` integer,
863
- `prompt_tokens` integer,
864
- `completion_tokens` integer,
865
- `cost_cents` integer,
866
- `raw_response` text,
867
- `triggered_by_id` text,
868
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
869
- `started_at` text,
870
- `completed_at` text,
871
- `error_message` text,
872
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
873
- FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
874
-);
875
-CREATE TABLE `pr_metrics` (
876
- `id` text PRIMARY KEY NOT NULL,
877
- `pull_request_id` text NOT NULL,
878
- `repository_id` text NOT NULL,
879
- `author_id` text NOT NULL,
880
- `time_to_first_review` integer,
881
- `time_to_approval` integer,
882
- `time_to_merge` integer,
883
- `total_cycle_time` integer,
884
- `review_rounds` integer DEFAULT 1,
885
- `reviewers_count` integer DEFAULT 0,
886
- `comments_count` integer DEFAULT 0,
887
- `changes_requested_count` integer DEFAULT 0,
888
- `lines_added` integer DEFAULT 0,
889
- `lines_removed` integer DEFAULT 0,
890
- `files_changed` integer DEFAULT 0,
891
- `commits` integer DEFAULT 1,
892
- `is_stacked` integer DEFAULT false,
893
- `stack_position` integer,
894
- `pr_created_at` text,
895
- `first_review_at` text,
896
- `approved_at` text,
897
- `merged_at` text,
898
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
899
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
900
- FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade,
901
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
902
- FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
903
-);
904
-CREATE TABLE `repo_metrics` (
905
- `id` text PRIMARY KEY NOT NULL,
906
- `repository_id` text NOT NULL,
907
- `week_of` text NOT NULL,
908
- `prs_opened` integer DEFAULT 0,
909
- `prs_merged` integer DEFAULT 0,
910
- `prs_closed` integer DEFAULT 0,
911
- `avg_time_to_first_review` integer,
912
- `avg_time_to_merge` integer,
913
- `avg_review_rounds` integer,
914
- `avg_lines_changed` integer,
915
- `avg_files_changed` integer,
916
- `stacked_prs` integer DEFAULT 0,
917
- `stacked_prs_percentage` integer DEFAULT 0,
918
- `active_authors` integer DEFAULT 0,
919
- `active_reviewers` integer DEFAULT 0,
920
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
921
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
922
-);
923
-CREATE TABLE `review_metrics` (
924
- `id` text PRIMARY KEY NOT NULL,
925
- `user_id` text NOT NULL,
926
- `repository_id` text,
927
- `organization_id` text,
928
- `week_of` text NOT NULL,
929
- `prs_authored` integer DEFAULT 0,
930
- `prs_authored_merged` integer DEFAULT 0,
931
- `avg_time_to_merge_authored` integer,
932
- `lines_authored_added` integer DEFAULT 0,
933
- `lines_authored_removed` integer DEFAULT 0,
934
- `prs_reviewed` integer DEFAULT 0,
935
- `avg_time_to_review` integer,
936
- `comments_given` integer DEFAULT 0,
937
- `approvals_given` integer DEFAULT 0,
938
- `changes_requested_given` integer DEFAULT 0,
939
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
940
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
941
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
942
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade,
943
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action
944
-);
945
-CREATE TABLE `slack_channel_mappings` (
946
- `id` text PRIMARY KEY NOT NULL,
947
- `workspace_id` text NOT NULL,
948
- `repository_id` text NOT NULL,
949
- `channel_id` text NOT NULL,
950
- `channel_name` text,
951
- `notify_on` text DEFAULT '["pr_created","pr_merged","review_requested","ci_failed"]',
952
- `notify_branches` text,
953
- `notify_authors` text,
954
- `is_active` integer DEFAULT true,
955
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
956
- `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
957
- FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade,
958
- FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
959
-);
960
-CREATE TABLE `slack_user_mappings` (
961
- `id` text PRIMARY KEY NOT NULL,
962
- `user_id` text NOT NULL,
963
- `workspace_id` text NOT NULL,
964
- `slack_user_id` text NOT NULL,
965
- `slack_username` text,
966
- `dm_preferences` text DEFAULT '{"review_requested":true,"pr_approved":true,"ci_failed":true}',
967
- `dnd_enabled` integer DEFAULT false,
968
- `dnd_start` text,
969
- `dnd_end` text,
970
- `dnd_timezone` text,
971
- `is_active` integer DEFAULT true,
972
- `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
973
- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
974
- FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade
975
-);
976
-CREATE TABLE `slack_workspaces` (
977
- `id` text PRIMARY KEY NOT NULL,
978
- `organization_id` text,
979
- `team_id` text NOT NULL,
980
- `team_name` text,
981
- `team_domain` text,
982
- `access_token` text NOT NULL,
983
- `bot_user_id` text,
984
- `bot_access_token` text,
985
- `scopes` text,
986
- `installed_by_id` text NOT NULL,
987
- `installed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,
988
- `is_active` integer DEFAULT true,
989
- `last_used_at` text,
990
- FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade,
991
- FOREIGN KEY (`installed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
992
-);
993
0