Repository navigation
feat(workspace): document portability and private reuse #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
7f8b367
feat(workspace): document portability and private reuse
Programcode 308db87
test(workspace): lock portability boundaries
Programcode b9eefdc
test(workspace): verify portability boundaries
Programcode e7b032d
fix(workspace): harden Google portability
Programcode e86ba83
fix(workspace): serialize Google document creation
Programcode 4ca8a87
test(workspace): cover portability journey
Programcode 19d2b8a
fix(workspace): bound Google portability claims
Programcode 580fd98
chore(workspace): add private trial lifecycle
Programcode 64381e7
fix(workspace): accept private proxy host
Programcode 35e9128
fix(workspace): validate raw trial host
Programcode 9d729a6
fix(workspace): verify private trial over tailnet IP
Programcode bab3a8a
test(workspace): exercise the private trial journey
Programcode cfc3911
fix(workspace): deliver portability downloads reliably
Programcode 43cc9ae
fix(workspace): generate DOCX in browsers
Programcode 7f58ee6
fix(workspace): export untouched documents safely
Programcode c0583e7
fix(workspace): own private trial proxy cleanup
Programcode b112c03
fix(workspace): satisfy Google docs lint
Programcode d52598c
fix(workspace): route Deno boundary tests correctly
Programcode File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| VITE_SENTRY_DSN= | ||
| VITE_POSTHOG_KEY= | ||
| VITE_POSTHOG_HOST=https://eu.i.posthog.com | ||
| VITE_GOOGLE_PICKER_API_KEY= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| map $http_upgrade $connection_upgrade { | ||
| default upgrade; | ||
| '' close; | ||
| } | ||
|
|
||
| server { | ||
| listen 127.0.0.1:18480; | ||
| server_name codex-remote.tailab0ab3.ts.net; | ||
|
|
||
| if ($http_host !~ ^(codex-remote\.tailab0ab3\.ts\.net(:18443)?|127\.0\.0\.1:18480)$) { return 444; } | ||
|
|
||
| root /usr/share/nginx/html; | ||
| index index.html; | ||
|
|
||
| add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' https://*.jam.dev; worker-src 'self' blob:; frame-src 'self' https://accounts.google.com https://*.jam.dev; connect-src 'self' wss://codex-remote.tailab0ab3.ts.net:18443 https://*.jam.dev https://oauth2.googleapis.com https://www.googleapis.com;" always; | ||
| add_header X-Content-Type-Options "nosniff" always; | ||
| add_header Referrer-Policy "no-referrer" always; | ||
| add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; | ||
| add_header X-Robots-Tag "noindex, nofollow" always; | ||
|
|
||
| location ^~ /supabase/ { | ||
| proxy_pass http://127.0.0.1:54321/; | ||
| proxy_set_header Host 127.0.0.1:54321; | ||
| proxy_set_header X-Forwarded-Proto https; | ||
| proxy_set_header X-Forwarded-Host $host; | ||
| } | ||
|
|
||
| location = /collaboration { | ||
| proxy_pass http://127.0.0.1:12345; | ||
| proxy_http_version 1.1; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| proxy_set_header Connection $connection_upgrade; | ||
| proxy_set_header Host 127.0.0.1:12345; | ||
| proxy_read_timeout 1h; | ||
| } | ||
|
|
||
| location ^~ /access/ { | ||
| proxy_pass http://127.0.0.1:12345; | ||
| proxy_set_header Host 127.0.0.1:12345; | ||
| } | ||
|
|
||
| location ^~ /review/ { | ||
| proxy_pass http://127.0.0.1:12345; | ||
| proxy_set_header Host 127.0.0.1:12345; | ||
| } | ||
|
|
||
| location ~ /\. { return 404; } | ||
| location ~* \.(?:env|log|lock|md|ya?ml)$ { return 404; } | ||
| location / { try_files $uri $uri/ /index.html; } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| source "$DIR/lib/lock.sh" | ||
|
|
||
| DB_LOCK="$HOME/.superset/debatelab-db.lock" | ||
| TRIAL_ORIGIN="https://codex-remote.tailab0ab3.ts.net:18443" | ||
| APP_PORT=18480 | ||
| COLLABORATION_PORT=12345 | ||
| SERVE_PORT=18443 | ||
| PROXY_NAME="debatelab-workspace-trial-proxy-$$" | ||
|
|
||
| runtime_dir="" | ||
| stack_started=false | ||
| serve_started=false | ||
| proxy_started=false | ||
| owned_pids=() | ||
|
|
||
| stop_owned_processes() { | ||
| local index | ||
| if [ "$proxy_started" = true ]; then | ||
| docker rm --force "$PROXY_NAME" >/dev/null 2>&1 || true | ||
| fi | ||
| for ((index=${#owned_pids[@]}-1; index>=0; index--)); do | ||
| kill -- "-${owned_pids[$index]}" 2>/dev/null || true | ||
| done | ||
| for ((index=${#owned_pids[@]}-1; index>=0; index--)); do | ||
| wait "${owned_pids[$index]}" 2>/dev/null || true | ||
| done | ||
| } | ||
|
|
||
| cleanup() { | ||
| local status=$? | ||
| trap - EXIT INT TERM | ||
| if [ "$serve_started" = true ]; then | ||
| tailscale serve --https="$SERVE_PORT" off >/dev/null 2>&1 || status=1 | ||
| fi | ||
| stop_owned_processes | ||
| if [ "$stack_started" = true ] && ! supabase stop; then | ||
| status=1 | ||
| fi | ||
| if [ -n "$runtime_dir" ] && [ -d "$runtime_dir" ]; then | ||
| rm -r -- "$runtime_dir" | ||
| fi | ||
| lock_release "$DB_LOCK" | ||
| exit "$status" | ||
| } | ||
|
|
||
| for port in "$APP_PORT" "$COLLABORATION_PORT"; do | ||
| if ss -ltn "sport = :$port" | tail -n +2 | grep -q .; then | ||
| echo "Workspace trial port $port is already in use" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
| if tailscale serve status --json | node -e 'const s=JSON.parse(require("fs").readFileSync(0,"utf8")); process.exit(s.TCP?.[process.argv[1]] ? 0 : 1)' "$SERVE_PORT"; then | ||
| echo "Tailscale Serve port $SERVE_PORT is already configured" >&2 | ||
| exit 1 | ||
| fi | ||
| if supabase status -o json >/dev/null 2>&1; then | ||
| echo "A local Supabase stack is already running" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| lock_acquire "$DB_LOCK" 600 | ||
| trap cleanup EXIT INT TERM | ||
| runtime_dir="$(mktemp -d)" | ||
|
|
||
| supabase start >"$runtime_dir/supabase.log" 2>&1 | ||
| stack_started=true | ||
| status_json="$(supabase status -o json 2>/dev/null)" | ||
| status_value() { | ||
| node -e 'const value=JSON.parse(process.argv[1])[process.argv[2]]; if (!value) process.exit(1); process.stdout.write(String(value))' "$status_json" "$1" | ||
| } | ||
| api_url="$(status_value API_URL)" | ||
| anon_key="$(status_value ANON_KEY)" | ||
| service_role_key="$(status_value SERVICE_ROLE_KEY)" | ||
| database_url="$(status_value DB_URL)" | ||
| project_id="$(basename "$(pwd)")" | ||
| mailer_host="$(docker ps --filter "name=supabase_inbucket_$project_id" --format '{{.Names}}')" | ||
| if [ -z "$mailer_host" ]; then | ||
| echo "Local Mailpit container is unavailable" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| LOCAL_SERVICE_ROLE_KEY="$service_role_key" \ | ||
| LOCAL_MAILER_HOST="$mailer_host" \ | ||
| node scripts/workspace-test-env.mjs .superset/workspace-test.env "$runtime_dir/function.env" "$TRIAL_ORIGIN" | ||
|
|
||
| setsid supabase functions serve workspace-invites --no-verify-jwt \ | ||
| --env-file "$runtime_dir/function.env" >"$runtime_dir/functions.log" 2>&1 & | ||
| owned_pids+=("$!") | ||
|
|
||
| COLLABORATION_DATABASE_URL="$database_url" \ | ||
| COLLABORATION_PORT="$COLLABORATION_PORT" \ | ||
| COLLABORATION_ALLOWED_ORIGINS="$TRIAL_ORIGIN" \ | ||
| SUPABASE_URL="$api_url" \ | ||
| SUPABASE_ANON_KEY="$anon_key" \ | ||
| setsid npm run start:collaboration >"$runtime_dir/collaboration.log" 2>&1 & | ||
| owned_pids+=("$!") | ||
|
|
||
| VITE_LOCAL_PREVIEW=true \ | ||
| VITE_LOCAL_SUPABASE_URL="$TRIAL_ORIGIN/supabase" \ | ||
| VITE_LOCAL_SUPABASE_ANON_KEY="$anon_key" \ | ||
| VITE_COLLABORATION_URL="wss://codex-remote.tailab0ab3.ts.net:$SERVE_PORT/collaboration" \ | ||
| npm run build | ||
|
|
||
| setsid docker run --rm --name "$PROXY_NAME" --network host \ | ||
| --volume "$PWD/dist:/usr/share/nginx/html:ro" \ | ||
| --volume "$DIR/workspace-trial-nginx.conf:/etc/nginx/conf.d/default.conf:ro" \ | ||
| nginx:alpine >"$runtime_dir/proxy.log" 2>&1 & | ||
| owned_pids+=("$!") | ||
| proxy_started=true | ||
|
|
||
| for _ in $(seq 1 200); do | ||
| if curl --fail --silent --output /dev/null --header "Host: codex-remote.tailab0ab3.ts.net:$SERVE_PORT" "http://127.0.0.1:$APP_PORT"; then break; fi | ||
| sleep 0.1 | ||
| done | ||
| curl --fail --silent --output /dev/null --header "Host: codex-remote.tailab0ab3.ts.net:$SERVE_PORT" "http://127.0.0.1:$APP_PORT" | ||
|
|
||
| tailscale serve --bg --https="$SERVE_PORT" "http://127.0.0.1:$APP_PORT" | ||
| serve_started=true | ||
| tailscale_ip="$(tailscale ip -4)" | ||
| curl --fail --silent --output /dev/null \ | ||
| --resolve "codex-remote.tailab0ab3.ts.net:$SERVE_PORT:$tailscale_ip" \ | ||
| "$TRIAL_ORIGIN" | ||
|
|
||
| echo "Workspace trial ready: $TRIAL_ORIGIN" | ||
| echo "Commit: $(git rev-parse HEAD)" | ||
| echo "Stop with: systemctl --user stop debatelab-workspace-trial.service" | ||
|
|
||
| while kill -0 "${owned_pids[0]}" 2>/dev/null && kill -0 "${owned_pids[1]}" 2>/dev/null; do | ||
| sleep 5 | ||
| done | ||
| echo "A workspace trial process exited unexpectedly" >&2 | ||
| exit 1 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| import { readFile, writeFile } from "node:fs/promises"; | ||
|
|
||
| const [source, target] = process.argv.slice(2); | ||
| const [source, target, appOrigin] = process.argv.slice(2); | ||
| const serviceRoleKey = process.env.LOCAL_SERVICE_ROLE_KEY; | ||
| const mailerHost = process.env.LOCAL_MAILER_HOST; | ||
| if (!source || !target || !serviceRoleKey || !mailerHost) throw new Error("Local Edge environment is incomplete"); | ||
| const fixture = (await readFile(source, "utf8")).replace("workspace-mailer.invalid", mailerHost); | ||
| const fixture = (await readFile(source, "utf8")) | ||
| .replace("workspace-mailer.invalid", mailerHost) | ||
| .replaceAll("http://127.0.0.1:18081", appOrigin || "http://127.0.0.1:18081"); | ||
| await writeFile(target, `${fixture.trim()}\nWORKSPACE_INVITE_SERVICE_ROLE_KEY=${serviceRoleKey}\n`, { mode: 0o600 }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change introduces a Tailscale-served origin, three dedicated ports, and a new Nginx runtime boundary. The repository directive requires the owning focused documentation to be updated whenever commands, ports, workflows, or runtime boundaries change. That required documentation must be added before merging.
Context Used: AGENTS.md (source)
Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!