diff --git a/.claudeignore b/.claudeignore new file mode 100644 index 0000000..5391107 --- /dev/null +++ b/.claudeignore @@ -0,0 +1,58 @@ +# Spec Kitty Configuration and Templates +# These are internal directories that shouldn't be scanned by AI assistants + +# Template directories (not working code) +.kittify/templates/ +.kittify/missions/ +.kittify/scripts/ + +# Agent command directories (generated from templates, not source) +.claude/ +.codex/ +.gemini/ +.cursor/ +.qwen/ +.opencode/ +.windsurf/ +.kilocode/ +.augment/ +.roo/ +.amazonq/ +.github/copilot/ + +# Git metadata +.git/ + +# Build artifacts and caches +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ +.coverage +htmlcov/ +node_modules/ +dist/ +build/ +*.egg-info/ + +# Virtual environments +.venv/ +venv/ +env/ + +# OS-specific files +.DS_Store +Thumbs.db +desktop.ini + +# IDE directories +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Logs and databases +*.log +*.db +*.sqlite diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..07ef88a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +kitty-specs/**/status.events.jsonl merge=spec-kitty-event-log diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 99012bd..63c599d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - template: [user-defined-web, drupal-core, drupal-contrib, freeform] + template: [user-defined-web, drupal-core, freeform] fail-fast: false steps: - uses: actions/checkout@v6 diff --git a/.gitignore b/.gitignore index 7c5d86a..ba14d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,32 @@ __pycache__/ # Temp files composer.json.tmp + +# Added by Spec Kitty CLI (auto-managed) +.claude/ +.codex/ +.vibe/ +.opencode/ +.windsurf/ +.gemini/ +.cursor/ +.qwen/ +.kilocode/ +.augment/ +.roo/ +.amazonq/ +.kiro/ +.agent/ +.github/copilot/ +.kittify/.dashboard +.kittify/charter/context-state.json +.kittify/charter/directives.yaml +.kittify/charter/governance.yaml +.kittify/charter/metadata.yaml +.kittify/charter/references.yaml +.kittify/dossiers/ +.kittify/events/ +.kittify/merge-state.json +.kittify/missions/__pycache__/ +.kittify/runtime/ +.kittify/workspaces/ diff --git a/.kittify/charter/charter.md b/.kittify/charter/charter.md new file mode 100644 index 0000000..1898eff --- /dev/null +++ b/.kittify/charter/charter.md @@ -0,0 +1,77 @@ +# Project Charter + + + +Generated: 2026-05-07T16:47:53Z + +## Testing Standards + +- Integration tests are BATS scripts that run against a live staging Coder instance (staging-coder.ddev.com) before any deployment to production. Plan-level tests use `terraform test` (no real infrastructure required). CI also runs `terraform fmt -check -recursive` and `terraform validate` for each touched template. There is no unit-test coverage percentage target — correctness is verified by staging promotion. + + +## Quality Gates + +- All changes require a pull request. CI must pass before merge. CI gates: `terraform fmt -check`, `terraform validate`, `terraform test` for each template. Integration test scripts run against staging before promoting to production. No direct commits to main. + + +## Performance Benchmarks + +- No quantitative latency targets. Workspace startup should complete within a reasonable time (under 5 minutes) as a qualitative target. CLI commands (terraform, make) should not timeout during CI runs. + + +## Branch Strategy + +- Every change requires a pull request. Single maintainer project currently, but PRs still required for traceability and CI gating. Conventional commit messages are preferred. + +- Deployment constraints: Two environments: staging-coder.ddev.com and production coder.ddev.com. Templates are promoted staging → production only after integration tests pass on staging. The main branch reflects the production-ready state. Feature branches are used for all development work. + + +## Governance Activation + +```yaml +mission: software-dev +selected_paradigms: [] +selected_directives: [] +available_tools: [git, spec-kitty] +template_set: software-dev-default +``` + +## Policy Summary + +- Intent: Manage and deploy coder.ddev.com — a cloud-hosted DDEV environment for web developers. The project owns the Coder workspace templates (Terraform HCL), the base Docker image, and the supporting shell scripts that enable Docker-in-Docker development via Sysbox runtime. A breaking change means developers cannot create or use workspaces; risk level is high for end users who depend on the service. + +- Languages/Frameworks: Terraform (HCL), Shell (Bash), Dockerfile (Ubuntu 24.04 base), bats-core (BATS test framework for shell-level integration tests), Go toolchain (available in image for future use). + +- Testing: Integration tests are BATS scripts that run against a live staging Coder instance (staging-coder.ddev.com) before any deployment to production. Plan-level tests use `terraform test` (no real infrastructure required). CI also runs `terraform fmt -check -recursive` and `terraform validate` for each touched template. There is no unit-test coverage percentage target — correctness is verified by staging promotion. + +- Quality Gates: All changes require a pull request. CI must pass before merge. CI gates: `terraform fmt -check`, `terraform validate`, `terraform test` for each template. Integration test scripts run against staging before promoting to production. No direct commits to main. + +- Review Policy: Every change requires a pull request. Single maintainer project currently, but PRs still required for traceability and CI gating. Conventional commit messages are preferred. + +- Performance Targets: No quantitative latency targets. Workspace startup should complete within a reasonable time (under 5 minutes) as a qualitative target. CLI commands (terraform, make) should not timeout during CI runs. + +- Deployment Constraints: Two environments: staging-coder.ddev.com and production coder.ddev.com. Templates are promoted staging → production only after integration tests pass on staging. The main branch reflects the production-ready state. Feature branches are used for all development work. + + +## Project Directives + +1. Respect risk boundaries: High-risk changes: modifications to startup scripts, Sysbox runtime configuration, Docker daemon setup, or base image layers — these affect all workspaces. Medium-risk: template variable changes or new templates. Low-risk: documentation, Makefile targets, version bumps. Any change that affects running workspaces requires staging validation. + +2. Keep documentation synchronized with workflow and behavior changes. + +## Reference Index + +| Reference ID | Kind | Summary | Local Doc | +|---|---|---|---| +| `USER:PROJECT_PROFILE` | user_profile | Project-specific interview answers captured for charter compilation. | `_LIBRARY/user-project-profile.md` | +| `TEMPLATE_SET:software-dev-default` | template_set | Build high-quality software with structured workflows and test-driven development | `_LIBRARY/template-set-software-dev-default.md` | + +## Amendment Process + +Charter amendments follow the same PR process as code changes. Significant governance changes should be discussed in the PR description. + + +## Exception Policy + +Hotfixes for production incidents may bypass staging testing with explicit maintainer acknowledgment in the PR, but must still pass CI. + diff --git a/.kittify/charter/interview/answers.yaml b/.kittify/charter/interview/answers.yaml new file mode 100644 index 0000000..333c05e --- /dev/null +++ b/.kittify/charter/interview/answers.yaml @@ -0,0 +1,79 @@ +schema_version: "1.0.0" +mission: "software-dev" +profile: "comprehensive" +answers: + project_intent: > + Manage and deploy coder.ddev.com — a cloud-hosted DDEV environment for + web developers. The project owns the Coder workspace templates (Terraform HCL), + the base Docker image, and the supporting shell scripts that enable + Docker-in-Docker development via Sysbox runtime. A breaking change means + developers cannot create or use workspaces; risk level is high for end users + who depend on the service. + + languages_frameworks: > + Terraform (HCL), Shell (Bash), Dockerfile (Ubuntu 24.04 base), + bats-core (BATS test framework for shell-level integration tests), + Go toolchain (available in image for future use). + + testing_requirements: > + Integration tests are BATS scripts that run against a live staging Coder + instance (staging-coder.ddev.com) before any deployment to production. + Plan-level tests use `terraform test` (no real infrastructure required). + CI also runs `terraform fmt -check -recursive` and `terraform validate` + for each touched template. There is no unit-test coverage percentage target — + correctness is verified by staging promotion. + + quality_gates: > + All changes require a pull request. CI must pass before merge. + CI gates: `terraform fmt -check`, `terraform validate`, `terraform test` + for each template. Integration test scripts run against staging before + promoting to production. No direct commits to main. + + review_policy: > + Every change requires a pull request. Single maintainer project currently, + but PRs still required for traceability and CI gating. Conventional commit + messages are preferred. + + performance_targets: > + No quantitative latency targets. Workspace startup should complete within + a reasonable time (under 5 minutes) as a qualitative target. CLI commands + (terraform, make) should not timeout during CI runs. + + deployment_constraints: > + Two environments: staging-coder.ddev.com and production coder.ddev.com. + Templates are promoted staging → production only after integration tests + pass on staging. The main branch reflects the production-ready state. + Feature branches are used for all development work. + + documentation_policy: > + User-facing documentation lives in /docs/. CLAUDE.md documents developer + and contributor guidance. Changes to template behavior or CLI commands + must be reflected in docs before merge. Doc-only changes are low-risk + but still require PRs. + + risk_boundaries: > + High-risk changes: modifications to startup scripts, Sysbox runtime + configuration, Docker daemon setup, or base image layers — these affect + all workspaces. Medium-risk: template variable changes or new templates. + Low-risk: documentation, Makefile targets, version bumps. + Any change that affects running workspaces requires staging validation. + + amendment_process: > + Charter amendments follow the same PR process as code changes. + Significant governance changes should be discussed in the PR description. + + exception_policy: > + Hotfixes for production incidents may bypass staging testing with explicit + maintainer acknowledgment in the PR, but must still pass CI. + +selected_paradigms: [] + +selected_directives: [] + +available_tools: + - git + - spec-kitty + - terraform + - docker + - make + - bats diff --git a/.kittify/config.yaml b/.kittify/config.yaml new file mode 100644 index 0000000..f8e6581 --- /dev/null +++ b/.kittify/config.yaml @@ -0,0 +1,11 @@ +vcs: + type: git +agents: + available: + - claude + auto_commit: true +project: + uuid: 595dcc83-8eee-4a87-8644-84d94a4b531c + slug: coder-ddev + node_id: a1b8182ad705 + build_id: ea439b9d-ce91-4e8c-a458-0f28a54b80bc diff --git a/.kittify/memory/templates/POWERSHELL_SYNTAX.md b/.kittify/memory/templates/POWERSHELL_SYNTAX.md new file mode 100644 index 0000000..a2f1d2d --- /dev/null +++ b/.kittify/memory/templates/POWERSHELL_SYNTAX.md @@ -0,0 +1,245 @@ +# PowerShell Syntax Guide for AI Agents + +**⚠️ READ THIS if you are working in a PowerShell environment** + +This guide helps AI agents use correct PowerShell syntax when working with spec-kitty workflows. + +--- + +## Quick Reference: Bash vs PowerShell + +| Task | ❌ Bash (WRONG) | ✅ PowerShell (CORRECT) | +|------|-----------------|-------------------------| +| **Command chaining** | `cmd1 && cmd2` | `cmd1; cmd2` | +| **Parameter flags** | `--json --paths-only` | `-Json -PathsOnly` | +| **Script path** | `./scripts/bash/script.sh` | `..\scripts\powershell\Script.ps1` | +| **Environment variable** | `$VAR_NAME` | `$env:VAR_NAME` | +| **Current directory** | `pwd` | `Get-Location` (or `pwd` alias) | +| **List files** | `ls -la` | `Get-ChildItem` (or `ls` alias) | +| **File exists check** | `[ -f file.txt ]` | `Test-Path file.txt` | +| **Directory separator** | `/path/to/file` | `\path\to\file` | +| **Home directory** | `~/projects` | `$HOME\projects` | + +--- + +## Location Verification (PowerShell) + +**Check your current location:** + +```powershell +Get-Location +git branch --show-current +``` + +**Expected for mission worktrees:** + +- Location: `C:\Users\...\project\.worktrees\001-mission-name` +- Branch: `001-mission-name` (NOT `main`) + +--- + +## Running Spec-Kitty Commands (PowerShell) + +### Using the spec-kitty CLI + +Spec-kitty uses a Python CLI that works across all platforms: + +**Common commands:** + +- `spec-kitty agent mission create-mission ` - Create a new feature +- `spec-kitty verify-setup` - Check environment and paths +- `spec-kitty agent workflow implement --agent ` - Start implementing a work package +- `spec-kitty agent workflow review --agent ` - Start reviewing a work package +- `spec-kitty agent tasks move-task --to for_review` - Complete implementation (move to review) +- `spec-kitty merge` - Merge completed mission + +### Parameter Naming Convention + +PowerShell uses **PascalCase** with leading dash: + +- `-Json` (not `--json`) +- `-MissionName` (not `--mission-name`) +- `-IncludeTasks` (not `--include-tasks`) +- `-RequireTasks` (not `--require-tasks`) + +### Examples + +**Create feature:** + +```powershell +.\.kittify\scripts\powershell\Create-NewMission.ps1 ` + -MissionName "User Authentication" ` + -FeatureDescription "Add login and registration" +``` + +**Check prerequisites:** + +```powershell +.\.kittify\scripts\powershell\check-prerequisites.ps1 -Json -IncludeTasks +``` + +**Move task to review (after implementation):** + +```powershell +# Using the CLI (recommended): +spec-kitty agent tasks move-task WP01 --to for_review --note "Ready for review" + +# Or using PowerShell script: +.\.kittify\scripts\powershell\Move-TaskToLane.ps1 ` + -Feature "001-auth" ` + -TaskId "WP01" ` + -Lane "for_review" ` + -ShellPid $PID ` + -Agent "claude" +``` + +--- + +## Common Mistakes to Avoid + +### ❌ Don't Use Bash Operators + +```powershell +# WRONG: +cd worktrees && pwd + +# CORRECT: +cd worktrees; Get-Location +``` + +### ❌ Don't Use Bash-Style Parameters + +```powershell +# WRONG: +.\check-prerequisites.ps1 --json --require-tasks + +# CORRECT: +.\check-prerequisites.ps1 -Json -RequireTasks +``` + +### Path Separators in PowerShell + +PowerShell on Windows uses backslashes for native paths: + +```powershell +# WRONG (Unix-style paths on Windows): +cd ./.kittify/memory + +# CORRECT (Windows-style paths): +cd .\.kittify\memory +``` + +Note: Git commands work with forward slashes, but native PowerShell file operations expect backslashes. The spec-kitty CLI handles this automatically. + +--- + +## Environment Variables + +**Setting variables:** + +```powershell +$env:SPEC_KITTY_TEMPLATE_ROOT = "C:\path\to\spec-kitty" +``` + +**Reading variables:** + +```powershell +echo $env:SPEC_KITTY_TEMPLATE_ROOT +``` + +**Checking if set:** + +```powershell +if ($env:SPEC_KITTY_TEMPLATE_ROOT) { + Write-Host "Variable is set" +} +``` + +--- + +## File Operations + +**Check if file exists:** + +```powershell +if (Test-Path "spec.md") { + Write-Host "Spec exists" +} +``` + +**Read file:** + +```powershell +$content = Get-Content "spec.md" -Raw +``` + +**Create directory:** + +```powershell +New-Item -ItemType Directory -Path "tasks\planned" -Force +``` + +--- + +## Workflow Tips + +1. **Always use full parameter names** in scripts (not abbreviations) +2. **Use semicolons** to chain commands, not `&&` or `||` +3. **Backslashes** for local paths, forward slashes OK for git operations +4. **$PID** contains current PowerShell process ID (use for --shell-pid) +5. **Tab completion** works for parameter names in PowerShell + +--- + +## When to Use What + +**Use PowerShell scripts when:** + +- User specified `--script ps` during init +- You're in a Windows PowerShell terminal +- Templates reference `.ps1` files in frontmatter + +**Use Bash scripts when:** + +- User specified `--script sh` during init +- You're in bash/zsh/fish terminal +- Templates reference `.sh` files in frontmatter + +**Using spec-kitty commands:** +All spec-kitty commands work the same way on PowerShell and Bash: + +```powershell +spec-kitty agent workflow implement WP01 --agent claude # Auto-moves to doing +spec-kitty agent tasks move-task WP01 --to for_review # Completion step +spec-kitty verify-setup +spec-kitty dashboard +``` + +The CLI is cross-platform and handles path differences automatically. + +--- + +## Debugging PowerShell Issues + +**Common errors and solutions:** + +1. **"Parameter cannot be found that matches parameter name"** + - You used bash-style parameters (`--json`) + - Fix: Use PowerShell style (`-Json`) + +2. **"The term '&&' is not recognized"** + - You used bash command chaining + - Fix: Use semicolon (`;`) instead + +3. **"Cannot find path"** + - You used forward slashes in PowerShell path + - Fix: Use backslashes (`\`) for local paths + +4. **"Unable to import mission module"** + - Python can't find specify_cli package + - Check: `pip show spec-kitty-cli` + - Fix: Reinstall or check virtual environment + +--- + +**For full spec-kitty documentation, see the main templates and README.** diff --git a/.kittify/metadata.yaml b/.kittify/metadata.yaml new file mode 100644 index 0000000..1863372 --- /dev/null +++ b/.kittify/metadata.yaml @@ -0,0 +1,14 @@ +# Spec Kitty Project Metadata +# Auto-generated by spec-kitty init/upgrade +# DO NOT EDIT MANUALLY + +spec_kitty: + version: 3.1.8 + initialized_at: '2026-05-07T10:43:17.519511' + last_upgraded_at: null +environment: + python_version: 3.14.4 + platform: darwin + platform_version: macOS-26.4.1-arm64-arm-64bit-Mach-O +migrations: + applied: [] diff --git a/.kittify/skills-manifest.json b/.kittify/skills-manifest.json new file mode 100644 index 0000000..5a35257 --- /dev/null +++ b/.kittify/skills-manifest.json @@ -0,0 +1,288 @@ +{ + "version": 1, + "created_at": "2026-05-07T16:43:17.476272+00:00", + "updated_at": "2026-05-07T16:43:17.506903+00:00", + "spec_kitty_version": "3.1.8", + "entries": [ + { + "skill_name": "ad-hoc-profile-load", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/ad-hoc-profile-load/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:85acce1cc660cdec6f0fe8fd669d82b393572d9c5cda561ffd17df3f7b59fc5e", + "installed_at": "2026-05-07T16:43:17.480986+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-bulk-edit-classification", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-bulk-edit-classification/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:30e444e44afc5db2b44c8a57627f9f69537816d8c7c9fea7049efed71cdd410e", + "installed_at": "2026-05-07T16:43:17.482823+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-charter-doctrine", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-charter-doctrine/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:c46f7804b175a3e185bf7d22f792bd80b1e67f798d8f9fc40ee58964838e1da0", + "installed_at": "2026-05-07T16:43:17.485117+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-charter-doctrine", + "source_file": "references/charter-command-map.md", + "installed_path": ".claude/skills/spec-kitty-charter-doctrine/references/charter-command-map.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:8d2cccd8dc727cb9ba78c2e9bf3559e83cdf93d6a632970254c9ae6b8b5733db", + "installed_at": "2026-05-07T16:43:17.485117+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-charter-doctrine", + "source_file": "references/doctrine-artifact-structure.md", + "installed_path": ".claude/skills/spec-kitty-charter-doctrine/references/doctrine-artifact-structure.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:f771e9e9e98d65aa4380b94e3f6a24e08d6c2cd3ca44ca1cba3bd14283af9f17", + "installed_at": "2026-05-07T16:43:17.485117+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-git-workflow", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-git-workflow/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:631f90114af7e9d108a58741af46dcd08163f3a814cbb9d95c905c19d337cc60", + "installed_at": "2026-05-07T16:43:17.487395+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-git-workflow", + "source_file": "references/git-operations-matrix.md", + "installed_path": ".claude/skills/spec-kitty-git-workflow/references/git-operations-matrix.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:6bb45291ff5ce626eaadde3a6e4a4c8d69577b3c4698c4551d251f293c66bfaa", + "installed_at": "2026-05-07T16:43:17.487395+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-glossary-context", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-glossary-context/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:e9592a088d2d6d2e2ecf65eaa6a8d31f0dbe1e6f4236d5747d6fe26ea980e89e", + "installed_at": "2026-05-07T16:43:17.489912+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-glossary-context", + "source_file": "references/glossary-field-guide.md", + "installed_path": ".claude/skills/spec-kitty-glossary-context/references/glossary-field-guide.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:9cddfccd64773976cf97450fa983dc8cc3aa7354a6002d763a7fa3ac65ff9ace", + "installed_at": "2026-05-07T16:43:17.489912+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-glossary-context", + "source_file": "references/semantic-drift-examples.md", + "installed_path": ".claude/skills/spec-kitty-glossary-context/references/semantic-drift-examples.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:d2df36b399316d12d05b10b36dab19a3eb330ed9c502b9b0fa2bb35ecf8341c4", + "installed_at": "2026-05-07T16:43:17.489912+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-implement-review", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-implement-review/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:6977839d92e84b4fb5da00e1c665003a95ead5fba92eaa9254ec78d074842f98", + "installed_at": "2026-05-07T16:43:17.492386+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-implement-review", + "source_file": "references/agent-dispatch-matrix.md", + "installed_path": ".claude/skills/spec-kitty-implement-review/references/agent-dispatch-matrix.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:94a2ddfb52a2b1da85e286221e580fd49d94cbb4e2a716fe02f4b76897c30ed0", + "installed_at": "2026-05-07T16:43:17.492386+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-implement-review", + "source_file": "references/rejection-loop-checklist.md", + "installed_path": ".claude/skills/spec-kitty-implement-review/references/rejection-loop-checklist.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:dcf6ffa8a173a5ea937a74fd1af4c0427f595008a00d91b5a7970a13f2e202ab", + "installed_at": "2026-05-07T16:43:17.492386+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-mission-review", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-mission-review/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:45ba9f04c2b9b337b7c27249a9779eb8613c0450ddd46adb12c11c02eaa80b5f", + "installed_at": "2026-05-07T16:43:17.494574+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-mission-review", + "source_file": "references/mission-review-fr-trace-guide.md", + "installed_path": ".claude/skills/spec-kitty-mission-review/references/mission-review-fr-trace-guide.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:4ae2391cd2e4c3bdb32a25e7a8476612fb1b94d156a4250701c9f537e011f0fd", + "installed_at": "2026-05-07T16:43:17.494574+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-mission-system", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-mission-system/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:b0172954384382df472ae94ae8754d1d98621530e1ae41336c1883a2827aafd0", + "installed_at": "2026-05-07T16:43:17.496533+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-mission-system", + "source_file": "references/mission-comparison-matrix.md", + "installed_path": ".claude/skills/spec-kitty-mission-system/references/mission-comparison-matrix.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:068063581aa360dcf114b32b65b31531367d4a7feacca7d607685a3862bfa3f4", + "installed_at": "2026-05-07T16:43:17.496533+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-orchestrator-api-operator", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-orchestrator-api-operator/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:750b35e440410841c41d811f26a59a3838b03458ff595b01ba594e9997ed2dd0", + "installed_at": "2026-05-07T16:43:17.499018+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-orchestrator-api-operator", + "source_file": "references/host-boundary-rules.md", + "installed_path": ".claude/skills/spec-kitty-orchestrator-api-operator/references/host-boundary-rules.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:fa806c1d0a1446b54ae589ea00f3edc55495bfaf43f801c90ca0872380e77fb0", + "installed_at": "2026-05-07T16:43:17.499018+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-orchestrator-api-operator", + "source_file": "references/orchestrator-api-contract.md", + "installed_path": ".claude/skills/spec-kitty-orchestrator-api-operator/references/orchestrator-api-contract.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:be5c0536a854771ace6c89db4e7b2f96c5f96e1f9a126f8a088941c1088c3a2d", + "installed_at": "2026-05-07T16:43:17.499018+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-runtime-next", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-runtime-next/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:4556cc9de0c88cf4b3b301b23b51deb39ed1338eecd5ee858988042d9dcc78aa", + "installed_at": "2026-05-07T16:43:17.501463+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-runtime-next", + "source_file": "references/blocked-state-recovery.md", + "installed_path": ".claude/skills/spec-kitty-runtime-next/references/blocked-state-recovery.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:787304873d5e8e254ea90ffd81e5af9d8d3c859a0688c7431f40fe4a3c20aa59", + "installed_at": "2026-05-07T16:43:17.501463+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-runtime-next", + "source_file": "references/runtime-result-taxonomy.md", + "installed_path": ".claude/skills/spec-kitty-runtime-next/references/runtime-result-taxonomy.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:80365f9eca8f07cd96a85aad55247707e90765caddc24037f1a60de4053df2fe", + "installed_at": "2026-05-07T16:43:17.501463+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-runtime-review", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-runtime-review/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:fd994f73054a11e98de7871526ea96df6e580e22e21a3b51d928498aaca02d6a", + "installed_at": "2026-05-07T16:43:17.503446+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-runtime-review", + "source_file": "references/review-checklist.md", + "installed_path": ".claude/skills/spec-kitty-runtime-review/references/review-checklist.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:9279016c3b96e71fbd5d06cbaedf719307fd4497afe1673af7a613a4e38b93ab", + "installed_at": "2026-05-07T16:43:17.503446+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-setup-doctor", + "source_file": "SKILL.md", + "installed_path": ".claude/skills/spec-kitty-setup-doctor/SKILL.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:8fb297bcb646a41abb0b8a024d842a956297ab12366bc6285a5bcacb880c6fb7", + "installed_at": "2026-05-07T16:43:17.506324+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-setup-doctor", + "source_file": "references/agent-path-matrix.md", + "installed_path": ".claude/skills/spec-kitty-setup-doctor/references/agent-path-matrix.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:6128bd3a1994467c24c168b9b998aa0b7c0d31a5f112c12aff4bebc5915416ff", + "installed_at": "2026-05-07T16:43:17.506324+00:00", + "delivery_mode": "symlink" + }, + { + "skill_name": "spec-kitty-setup-doctor", + "source_file": "references/common-failure-signatures.md", + "installed_path": ".claude/skills/spec-kitty-setup-doctor/references/common-failure-signatures.md", + "installation_class": "native-root-required", + "agent_key": "claude", + "content_hash": "sha256:fc5ccc9248a290f23507556d6cd25e2c38a63f8cba4cf4f40897735fd489198f", + "installed_at": "2026-05-07T16:43:17.506324+00:00", + "delivery_mode": "symlink" + } + ] +} diff --git a/docs/access-denied.html b/docs/access-denied.html new file mode 100644 index 0000000..9210df4 --- /dev/null +++ b/docs/access-denied.html @@ -0,0 +1,176 @@ + + + + + + Access Required — DDEV Coder Workspaces + + + + +
+
🔒
+

Access Required

+

+ Your GitHub account doesn't currently have access to coder.ddev.com. + Access is restricted to members of specific GitHub organizations. +

+ +
+

Who qualifies

+ +
+ + +
+ + + + diff --git a/docs/admin/coder-ddev-com/access-request-issue-template.yml b/docs/admin/coder-ddev-com/access-request-issue-template.yml new file mode 100644 index 0000000..e527b02 --- /dev/null +++ b/docs/admin/coder-ddev-com/access-request-issue-template.yml @@ -0,0 +1,44 @@ +# operator note: in the coder-ddev-com/access-requests repo, create +# .github/ISSUE_TEMPLATE/access-request.yml with this content. + +name: Access Request +description: Request access to coder.ddev.com +title: "Access request: [your GitHub username]" +labels: ["access-request"] +body: + - type: markdown + attributes: + value: | + Thanks for your interest in coder.ddev.com! Please fill out the form below. + + **Already have access?** If you are a member of the [ddev](https://github.com/ddev) org + or a $100+/month DDEV sponsor org, you should already have access — try signing in at + [coder.ddev.com](https://coder.ddev.com) first. + + - type: input + id: github_username + attributes: + label: GitHub username + description: Your GitHub username (the one you will use to sign in) + placeholder: "e.g. octocat" + validations: + required: true + + - type: textarea + id: reason + attributes: + label: Why do you want access? + description: Brief description — DDEV contributor, open-source developer, evaluating DDEV for a project, etc. + placeholder: "I maintain a DDEV add-on and want to test it in a cloud workspace..." + validations: + required: true + + - type: checkboxes + id: confirm + attributes: + label: Confirmation + options: + - label: I have checked that I do not already have access through the ddev org or a sponsor org + required: true + - label: I understand this is a shared resource and will use it responsibly + required: true diff --git a/docs/admin/coder-ddev-com/access-requests-README.md b/docs/admin/coder-ddev-com/access-requests-README.md new file mode 100644 index 0000000..86e0fb1 --- /dev/null +++ b/docs/admin/coder-ddev-com/access-requests-README.md @@ -0,0 +1,37 @@ + + +# coder-ddev-com / access-requests + +This repository is the access request tracker for [coder.ddev.com](https://coder.ddev.com). + +## What is coder.ddev.com? + +[coder.ddev.com](https://coder.ddev.com) is a cloud-hosted DDEV development environment for web developers. It provides workspaces running [DDEV](https://ddev.com) with VS Code for Web, a terminal, and full Docker support. + +## Who already has access? + +Access is automatically available (no request needed) if you are: + +- A member of the [ddev](https://github.com/ddev) GitHub org +- A member of an organization that sponsors DDEV at $100+/month + +See the [DDEV sponsors page](https://ddev.com/support-ddev/) for the current list of sponsors. + +## Requesting access + +If you do not have access through one of the above paths, open an issue in this repository using the **Access Request** template. + +We will review your request and, if approved, add you to the `coder-ddev-com` org. You will receive a GitHub invitation — once you accept it, you can sign in to coder.ddev.com with your GitHub account. + +**What to include in your request:** +- Your GitHub username +- Why you want access (brief description — DDEV user, contributor, etc.) + +## Response time + +We aim to respond to requests within a few business days. If you have not heard back in a week, feel free to ping the issue. + +## Questions + +Visit the [DDEV Discord](https://ddev.com/s/discord) or open an issue in [ddev/coder-ddev](https://github.com/ddev/coder-ddev/issues). diff --git a/docs/admin/coder-ddev-com/org-profile-README.md b/docs/admin/coder-ddev-com/org-profile-README.md new file mode 100644 index 0000000..7ebe3d1 --- /dev/null +++ b/docs/admin/coder-ddev-com/org-profile-README.md @@ -0,0 +1,32 @@ + + +# coder-ddev-com + +This organization is the managed access list for [coder.ddev.com](https://coder.ddev.com) — a cloud-based DDEV development environment for web developers. + +## What is coder.ddev.com? + +[coder.ddev.com](https://coder.ddev.com) provides cloud-hosted workspaces running [DDEV](https://ddev.com), an open-source local development environment tool for PHP, Node.js, Python, and more projects. Workspaces include VS Code for Web, a terminal, Docker-in-Docker, and full DDEV support — no local setup required. + +## How membership works + +Members of this organization can sign in to coder.ddev.com using GitHub OAuth. Membership is granted by a coder-ddev-com org owner. + +You do **not** need to make your membership public. Private membership is sufficient. + +## Who qualifies? + +Access is available to: + +- Members of the [ddev](https://github.com/ddev) GitHub org +- Members of organizations that sponsor DDEV at $100+/month +- Individuals approved by the DDEV maintainers (this org) + +## Requesting access + +If you do not have access through one of the paths above, open an issue in the [access-requests](https://github.com/coder-ddev-com/access-requests) repository. + +## Questions + +Visit the [DDEV Discord](https://discord.ddev.com) or open an issue in [ddev/ddev](https://github.com/ddev/ddev/issues). diff --git a/docs/admin/coder-ddev-com/sponsor-notification.md b/docs/admin/coder-ddev-com/sponsor-notification.md new file mode 100644 index 0000000..9a6f88f --- /dev/null +++ b/docs/admin/coder-ddev-com/sponsor-notification.md @@ -0,0 +1,36 @@ + + +Subject: coder.ddev.com access for [ORG NAME] org members + +Hi, + +As a $100+/month sponsor of DDEV, all members of the **[ORG NAME]** GitHub organization ([github.com/[GITHUB ORG SLUG]](https://github.com/[GITHUB ORG SLUG])) now have access to **[coder.ddev.com](https://coder.ddev.com)** — a cloud-hosted DDEV development environment. + +**What is coder.ddev.com?** + +coder.ddev.com provides on-demand cloud workspaces running DDEV with: +- VS Code for Web (full IDE in the browser) +- A terminal +- Docker-in-Docker via Sysbox (full DDEV support, all project types) + +No local setup required — spin up a workspace, start DDEV, and get coding. + +**How to access:** + +1. Visit [coder.ddev.com](https://coder.ddev.com) +2. Click **Sign in with GitHub** +3. Authorize the DDEV Coder app +4. You're in — create a workspace from the Drupal Core, Drupal Contrib, or Freeform template + +All members of the [ORG NAME] GitHub org can sign in. They do not need to be added individually. Org membership does not need to be public. + +**Questions or issues?** + +- [DDEV Discord](https://discord.ddev.com) — `#coder-ddev-com` channel (or any channel) +- [ddev/ddev issues](https://github.com/ddev/ddev/issues) + +Thank you for supporting DDEV! + +— The DDEV maintainers diff --git a/docs/admin/server-setup.md b/docs/admin/server-setup.md index b88fbc8..5735b41 100644 --- a/docs/admin/server-setup.md +++ b/docs/admin/server-setup.md @@ -659,15 +659,35 @@ The initial admin account must be created with username/password via the web UI **1. Create a GitHub OAuth App** -Create the app under your **GitHub organization**, not your personal account — apps created under a personal account show "by \" on the authorization screen instead of "by \". Go to **github.com/organizations/\/settings/applications → New OAuth App** and fill in: +Create the app under your **GitHub organization**, not your personal account — apps created under a personal account show "by \" on the authorization screen instead of "by \". Go to **github.com/organizations/ddev/settings/applications → New OAuth App** and fill in: -- **Application name**: `Coder (coder.ddev.com)` (or similar) +- **Application name**: `Coder (coder.ddev.com)` - **Homepage URL**: `https://coder.ddev.com` - **Authorization callback URL**: `https://coder.ddev.com/api/v2/users/oauth2/github/callback` - **Enable Device Flow**: leave unchecked (see note below) After creating the app, generate a client secret. Note the **Client ID** and **Client Secret**. +#### Two OAuth Apps: staging and production + +Register **two separate OAuth Apps** — one for staging, one for production. Separate apps isolate credentials between environments so that a staging secret compromise cannot affect production, and secret rotation on one environment does not require touching the other. + +**Staging app settings:** + +- **Application name**: `Coder (staging-coder.ddev.com)` +- **Homepage URL**: `https://staging-coder.ddev.com` +- **Authorization callback URL**: `https://staging-coder.ddev.com/api/v2/users/oauth2/github/callback` +- **Enable Device Flow**: leave unchecked + +**Production app settings** (same as above): + +- **Application name**: `Coder (coder.ddev.com)` +- **Homepage URL**: `https://coder.ddev.com` +- **Authorization callback URL**: `https://coder.ddev.com/api/v2/users/oauth2/github/callback` +- **Enable Device Flow**: leave unchecked + +Use the staging app's Client ID and Client Secret in `/etc/coder.d/coder.env` on staging-coder.ddev.com, and the production app's credentials on coder.ddev.com. + **2. Add to `/etc/coder.d/coder.env`** ```bash @@ -678,8 +698,9 @@ CODER_OAUTH2_GITHUB_CLIENT_SECRET=your-client-secret # Allow sign-ups via GitHub (new users are created automatically on first login) CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS=true -# Restrict to members of a specific GitHub org (recommended): -CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev +# Access control: ddev org members, coder-ddev-com managed list, and $100+/month sponsor orgs +# Institute-for-Advanced-Studies is included but not yet verified — confirm the org identity before deploying +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev,coder-ddev-com,tag1consulting,upsun,platformsh,Institute-for-Advanced-Studies,CPS-IT,redfinsolutions,Lullabot,b13,pixelandtonic,Cambrico,centarro,8mylez,dkd,liip,i-gelb,FameHelsinki,Gizra,mobilistics,OPTASY,passbolt,vaersaagod,affinitybridge,AGILEDROP,NPO-Applications-GmbH,AtenDesignGroup # Or allow any GitHub user (not recommended for a shared server): # CODER_OAUTH2_GITHUB_ALLOW_EVERYONE=true @@ -706,6 +727,82 @@ sudo systemctl restart coder There is also a toggle in the Coder admin UI at **Admin → Security** that can override the env var. Check that user sign-ups are not disabled there. +### Managing individual access via `coder-ddev-com` + +The `coder-ddev-com` GitHub organization is the managed access list for individuals who do not belong to the `ddev` org or one of the sponsor orgs. Adding someone to `coder-ddev-com` grants them signup access to coder.ddev.com without requiring a Coder server restart. + +**To grant access to an individual:** + +1. Go to **github.com/coder-ddev-com** → **People** → **Invite member** +2. Enter the person's GitHub username and send the invitation +3. Once they accept, they can log in to coder.ddev.com via GitHub OAuth + +**Initial members** (add these when creating the org): + +- `dougvann` — individual $100/month GitHub Sponsor +- `claudiu-cristea` — Webikon sponsor (linked as individual on ddev.com) +- Add LetsTalk, Amedick Sommer, and Pottkinder GmbH contacts when their GitHub usernames are confirmed + +**Note on private membership:** Users do not need to make their `coder-ddev-com` membership public. Private membership is sufficient — the `read:org` OAuth scope allows Coder to verify membership regardless of visibility setting. + +### Sponsor org access policy + +All $100+/month DDEV sponsors receive access as an org-level benefit: every member of a sponsor's GitHub org can sign in to coder.ddev.com without individual enrollment in `coder-ddev-com`. MacStadium and JetBrains are excluded (in-kind, not cash sponsors). + +| Company | GitHub org | Source | +| ------- | ---------- | ------ | +| Tag1 | `tag1consulting` | invoiced | +| Upsun | `upsun` | invoiced | +| Platform.sh (Upsun predecessor) | `platformsh` | invoiced | +| Institute for Advanced Studies | `Institute-for-Advanced-Studies` ⚠️ | invoiced | +| CPS-IT | `CPS-IT` | invoiced | +| Redfin Solutions | `redfinsolutions` | invoiced + featured | +| Lullabot | `Lullabot` | invoiced | +| B13 | `b13` | invoiced + featured | +| Pixel & Tonic (Craft CMS) | `pixelandtonic` | invoiced + featured | +| Cambrico | `Cambrico` | invoiced + featured | +| Centarro | `centarro` | invoiced + featured | +| 8mylez | `8mylez` | invoiced | +| dkd Internet Service GmbH | `dkd` | GitHub Sponsors | +| Liip | `liip` | GitHub Sponsors | +| i-gelb GmbH | `i-gelb` | featured | +| Fame Helsinki | `FameHelsinki` | featured | +| Gizra | `Gizra` | featured | +| mobilistics GmbH | `mobilistics` | featured | +| OPTASY | `OPTASY` | featured | +| Passbolt | `passbolt` | featured | +| Værsågod | `vaersaagod` | featured | +| Affinity Bridge | `affinitybridge` | featured | +| Agiledrop | `AGILEDROP` | featured | +| NPO Applications GmbH | `NPO-Applications-GmbH` | featured | +| Aten Design Group | `AtenDesignGroup` | featured | + +⚠️ `Institute-for-Advanced-Studies` — GitHub org exists but has no public name or description. Confirm with an operator that this is the correct org before deploying. + +Sponsors with no confirmed GitHub org (access via `coder-ddev-com` individual membership instead): LetsTalk, Amedick Sommer, Pottkinder GmbH. + +#### Adding a new sponsor org + +When a new organization reaches the $100+/month sponsorship level: + +1. **Find their GitHub org slug**: Check the sponsor's GitHub profile or ask them directly. Verify with `gh api orgs/ --jq '.login'` — if the command returns the slug, the org exists. + +2. **Add the slug to `ALLOWED_ORGS`** on both staging and production: + + ```bash + # On the server, edit /etc/coder.d/coder.env + # Append the new slug to CODER_OAUTH2_GITHUB_ALLOWED_ORGS (comma-separated, no spaces) + sudo systemctl restart coder + ``` + +3. **Test on staging first**: Ask someone from the org to attempt login on staging-coder.ddev.com before updating production. + +4. **Update the table above**: Add the org to the sponsor org table so the list stays accurate. + +5. **Notify the sponsor**: Send the sponsor notification (see `docs/admin/coder-ddev-com/sponsor-notification.md`) letting them know their org members now have access. + +**If the sponsor has no GitHub org**: Add their individual GitHub username to the `coder-ddev-com` org instead (see "Managing individual access" above). No server restart needed. + ### Fix cert permissions and test renewal Now that the `coder` system group exists, run the deploy hook you created in Step 6 to fix permissions on the certificate files: diff --git a/docs/admin/spec-kitty-workflow.md b/docs/admin/spec-kitty-workflow.md new file mode 100644 index 0000000..64f2c39 --- /dev/null +++ b/docs/admin/spec-kitty-workflow.md @@ -0,0 +1,103 @@ +# spec-kitty Workflow + +spec-kitty is the spec-driven development tool used for significant feature work in this project. It drives an AI agent through a structured lifecycle — discovery → research → spec → plan → implement → review — and keeps the paper trail in the repo. + +## When to use spec-kitty + +Use it for: +- New features or capabilities (new auth, new template type, new user-facing workflow) +- Breaking changes to infrastructure or user flows +- Work that touches multiple files and needs a traceable decision record + +Skip it for: bug fixes, typos, dependency bumps, config tweaks, one-line changes. + +## Project structure + +Two directories persist in the repo across all missions: + +``` +.kittify/ # Project-level config (set up once, shared by all missions) +├── charter/charter.md # Project governance, quality directives, stakeholder roles +├── config.yaml # spec-kitty project config +└── skills-manifest.json # Registered agent skills + +kitty-specs/ # One subdirectory per completed mission (permanent record) +└── / + ├── spec.md # Requirements document + ├── research.md # Research summary + ├── research/ # Evidence log + source register CSVs + ├── data-model.md # Entity model for the feature + ├── plan.md # Approved implementation plan + ├── wps.yaml # Work-package manifest (authoritative task source) + ├── tasks.md / tasks/WP*.md # Generated task files consumed by agents + ├── checklists/requirements.md # Requirement-traceability checklist + └── mission-events.jsonl # Append-only audit log of all state transitions +``` + +## Starting a new mission + +### 1. Create the feature scaffold + +```bash +spec-kitty specify +# e.g.: spec-kitty specify workspace-quotas +``` + +This creates `kitty-specs//` with the skeleton files and registers the mission. + +### 2. Run the agent + +From the repo root: + +```bash +spec-kitty next --agent claude --mission +``` + +The agent reads the current mission state and returns the next action. Keep running this command as the agent works through each phase. The state machine progresses through: + +``` +not_started → discovery → specify → plan → tasks_outline → tasks_packages + → tasks_finalize → implement → review → accept → done +``` + +**Important:** Always run `spec-kitty next` from the repo root, not from inside a worktree. + +### 3. Implementation lanes + +During implementation, spec-kitty creates git worktrees (`.worktrees/-lane-`) for parallel work packages. Each lane is an isolated branch. Edits to owned files **must happen inside the correct worktree**, not in the main repo checkout. After implementation: + +```bash +spec-kitty merge --mission --lane a # merge lane-a into target branch +spec-kitty merge --mission --lane b # merge lane-b, etc. +``` + +### 4. Review and accept + +After all work packages are implemented and merged: + +```bash +spec-kitty accept --mission +``` + +This validates completeness (all WPs done, all requirements traced) before the PR is opened. + +## Key files to know + +**`wps.yaml`** is the single source of truth for what needs to be built. It defines work packages, their dependencies, the files each WP owns, and the subtasks within each WP. The `finalize-tasks` step generates `tasks.md` and `lanes.json` from it. If you need to change scope, edit `wps.yaml` before finalize runs. + +**`spec.md`** contains the requirements (FR-xxx, C-xxx, NFR-xxx). Every requirement should trace to a WP in `wps.yaml` and appear in `checklists/requirements.md`. + +**`mission-events.jsonl`** is append-only. Never edit it directly — it's the audit log of every state transition. If spec-kitty rejects a `move-task` because the repo is dirty, commit any loose files first, then retry. + +## Common pitfalls + +- **Uncommitted files block transitions.** spec-kitty checks for a clean working tree before advancing state. Commit `mission-events.jsonl` and any other loose files before running `next` or `move-task`. +- **Git signing.** If the environment doesn't have an SSH signing agent, commits in external repos need `-c gpg.format=openpgp -c commit.gpgsign=false`. +- **`spec-kitty next` from the main repo.** Running it from inside a worktree fails with "must run from the main repository." +- **`--json` not supported everywhere.** The `implement` subcommand does not accept `--json`; drop the flag. + +## Completed missions + +| Mission ID | Title | PR | +|---|---|---| +| `github-org-gated-signup-01KR1P4G` | GitHub org-gated access for coder.ddev.com | #131 | diff --git a/docs/admin/user-management.md b/docs/admin/user-management.md index ccc3eff..e3ceb2f 100644 --- a/docs/admin/user-management.md +++ b/docs/admin/user-management.md @@ -487,6 +487,64 @@ coder ssh my-workspace -- coder gitssh --help coder users show ``` +## Access Management + +New account creation on coder.ddev.com and staging-coder.ddev.com is restricted to members of two GitHub organizations: the `ddev` org and the `coder-ddev-com` org. Additionally, members of $100+/month DDEV sponsor organizations can sign in directly. + +Existing accounts created before this restriction was applied remain active. Password authentication remains available for manually pre-created exception accounts. + +### Granting access via `coder-ddev-com` org membership + +The `coder-ddev-com` GitHub organization is the access list for individuals who are not members of the `ddev` org or a sponsor org. Adding someone to this org grants signup access without any Coder server change. + +**Steps:** + +1. Go to **github.com/coder-ddev-com** → **People** → **Invite member** +2. Enter the person's GitHub username and send the invitation +3. Once they accept the invitation, they can visit coder.ddev.com and sign in with GitHub + +**Note on private membership:** Users do not need to make their `coder-ddev-com` membership public. Private membership is sufficient — Coder uses the `read:org` OAuth scope to verify membership regardless of visibility. + +### Pre-creating password exception accounts + +For users who cannot authenticate via GitHub OAuth (e.g., users without a GitHub account, or accounts needed before org configuration is complete), an admin can pre-create a password-based account directly. + +**Via CLI:** + +```bash +# Create the account +coder users create --email user@example.com + +# Set a password interactively +coder users create --email user@example.com --set-password +``` + +**Via Web UI:** + +1. Log in as admin +2. Navigate to **Admin → Users → Create User** +3. Fill in username, email, and password +4. Assign the **Member** role + +The user logs in at coder.ddev.com with their username and password — GitHub OAuth is not required for password accounts. + +> **Important:** `CODER_DISABLE_PASSWORD_AUTH` must never be set to `true`. Password authentication must remain available for these exception accounts. + +### Private org membership and the `read:org` scope + +Users who are members of `ddev`, `coder-ddev-com`, or a sponsor org do not need to make that membership public. Coder requests the `read:org` GitHub OAuth scope, which allows it to verify private org membership during authentication. Users are prompted to grant this scope when they first click "Sign in with GitHub." + +### Initial `coder-ddev-com` members + +Add these individuals when creating the `coder-ddev-com` org: + +| GitHub username | Reason | +| --------------- | ------ | +| `dougvann` | Individual $100/month GitHub Sponsor | +| `claudiu-cristea` | Webikon sponsor (linked as individual on ddev.com) | + +The following sponsor contacts should also be added once their GitHub usernames are confirmed: LetsTalk, Amedick Sommer, Pottkinder GmbH. + ## Additional Resources - [Operations Guide](./operations-guide.md) - Template deployment and management diff --git a/docs/index.html b/docs/index.html index 53e828a..9f39f5c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -57,7 +57,7 @@ margin-bottom: 1.75rem; font-size: 1rem; color: #bbb; - max-width: 420px; + max-width: 500px; } .auth-callout svg { flex-shrink: 0; } @@ -252,7 +252,7 @@

Coder Workspaces

- Requires a GitHub accountsign in at coder.ddev.com first + Requires GitHub membership in the ddev org or a sponsor orgrequest access · sign in @@ -271,7 +271,7 @@

Coder Workspaces

How it works

    -
  1. Sign in with GitHub at coder.ddev.com
  2. +
  3. Sign in with GitHub at coder.ddev.com — access requires the ddev org, a $100+/mo sponsor org, or an approved request
  4. Click Create Workspace — your environment sets up automatically
  5. Click DDEV Web to open the running site, or VS Code to edit code
diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/checklists/requirements.md b/kitty-specs/github-org-gated-signup-01KR1P4G/checklists/requirements.md new file mode 100644 index 0000000..f6d0728 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/checklists/requirements.md @@ -0,0 +1,38 @@ +# Specification Quality Checklist: GitHub Org-Gated Signup + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-05-07 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Requirement types are separated (Functional / Non-Functional / Constraints) +- [x] IDs are unique across FR-###, NFR-###, and C-### entries +- [x] All requirement rows include a non-empty Status value +- [x] Non-functional requirements include measurable thresholds +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +All items pass. Spec is ready for `/spec-kitty.plan`. diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/data-model.md b/kitty-specs/github-org-gated-signup-01KR1P4G/data-model.md new file mode 100644 index 0000000..1c9b57e --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/data-model.md @@ -0,0 +1,98 @@ +# Data Model: GitHub Org-Gated Signup + +## Entities + +### GitHub OAuth App +Represents the custom DDEV-owned GitHub OAuth application registered under the `ddev` org. + +| Attribute | Type | Notes | +| --------- | ---- | ----- | +| `client_id` | string | Unique per environment (staging vs production) | +| `client_secret` | string | Secret; stored only in `/etc/coder.d/coder.env` on host | +| `owner_org` | string | Always `ddev` (institutional ownership) | +| `callback_url` | string | e.g. `https://coder.ddev.com/api/v2/users/oidc/callback` | +| `scopes` | string[] | `read:user`, `user:email`, `read:org` | + +**Notes**: Two separate OAuth App instances exist — one for staging, one for production. Credentials are never committed to this repo. + +--- + +### GitHub Organization +Represents a GitHub org whose members are allowed to create Coder accounts. + +| Attribute | Type | Notes | +| --------- | ---- | ----- | +| `org_slug` | string | GitHub org handle (e.g. `ddev`, `coder-ddev-com`) | +| `org_type` | enum | `core` \| `managed-access-list` \| `sponsor` | +| `access_method` | enum | `direct` — members sign in via org membership | +| `added_to_allowed_orgs` | bool | Whether the slug appears in `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` | + +**Org type meanings:** +- `core` — `ddev` org; all DDEV contributors. Primary access channel. +- `managed-access-list` — `coder-ddev-com` org; explicitly managed list for individuals outside `ddev`. +- `sponsor` — $100+/month sponsor orgs; members get access without individual enrollment in `coder-ddev-com`. + +**Current allowed orgs (27 total):** +`ddev`, `coder-ddev-com`, `tag1consulting`, `upsun`, `platformsh`, `Institute-for-Advanced-Studies`, `CPS-IT`, `redfinsolutions`, `Lullabot`, `b13`, `pixelandtonic`, `Cambrico`, `centarro`, `8mylez`, `dkd`, `liip`, `i-gelb`, `FameHelsinki`, `Gizra`, `mobilistics`, `OPTASY`, `passbolt`, `vaersaagod`, `affinitybridge`, `AGILEDROP`, `NPO-Applications-GmbH`, `AtenDesignGroup` + +--- + +### Coder User +Represents a user account on coder.ddev.com or staging-coder.ddev.com. + +| Attribute | Type | Notes | +| --------- | ---- | ----- | +| `username` | string | Coder account identifier | +| `auth_method` | enum | `github-oauth` \| `password` | +| `account_status` | enum | `existing` \| `new` | +| `github_org_membership` | string[] | Orgs the user belongs to (checked at signup) | + +**Notes**: Existing accounts created before org-gating are preserved. New signups require org membership. Password-auth accounts remain usable and are never disabled (`CODER_DISABLE_PASSWORD_AUTH` is not set). + +--- + +### Coder Server Environment +Represents the runtime configuration of one Coder server instance. + +| Attribute | Type | Notes | +| --------- | ---- | ----- | +| `config_file` | string | `/etc/coder.d/coder.env` | +| `environment` | enum | `staging` \| `production` | +| `CODER_OAUTH2_GITHUB_CLIENT_ID` | string | From OAuth App for this environment | +| `CODER_OAUTH2_GITHUB_CLIENT_SECRET` | string | From OAuth App for this environment | +| `CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS` | bool | Must be `true` | +| `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` | string | Comma-separated org slug list | +| `CODER_OAUTH2_GITHUB_DEVICE_FLOW` | bool | Must NOT be `true` (breaks `read:org`) | +| `CODER_DISABLE_PASSWORD_AUTH` | bool | Must NOT be `true` | + +--- + +## Relationships + +``` +Coder Server Environment + └─ uses ──► GitHub OAuth App (one per environment) + └─ allows ──► GitHub Organization (many, via ALLOWED_ORGS) + └─ grants access to ──► Coder User (new signups) + +Coder User + ├─ auth_method: github-oauth ──► must belong to at least one allowed GitHub org + └─ auth_method: password ──► manually pre-created; org membership not checked +``` + +--- + +## Configuration Scope + +This feature involves **no code changes** — only documentation and operator-applied server configuration: + +| Layer | Changes | +| ----- | ------- | +| Terraform templates | None | +| Docker image | None | +| Shell scripts | None | +| Docs (`docs/admin/server-setup.md`) | Update ALLOWED_ORGS, add staging OAuth section, document coder-ddev-com, add sponsor table and runbook | +| Docs (`docs/admin/user-management.md`) | Add Access Management section | +| Docs (`docs/admin/coder-ddev-com/`) | New directory: org profile README, access-requests repo README, issue template, sponsor notification | +| Docs (`docs/admin/blog-post-draft.md`) | Draft diff for ddev.com blog post | +| Server host (out of repo) | `/etc/coder.d/coder.env` on staging then production | diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/lanes.json b/kitty-specs/github-org-gated-signup-01KR1P4G/lanes.json new file mode 100644 index 0000000..213efbf --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/lanes.json @@ -0,0 +1,61 @@ +{ + "version": 1, + "mission_slug": "github-org-gated-signup-01KR1P4G", + "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", + "mission_branch": "kitty/mission-github-org-gated-signup-01KR1P4G", + "target_branch": "20260507_speckitty", + "lanes": [ + { + "lane_id": "lane-a", + "wp_ids": [ + "WP01" + ], + "write_scope": [ + "docs/admin/server-setup.md" + ], + "predicted_surfaces": [ + "api" + ], + "depends_on_lanes": [], + "parallel_group": 0 + }, + { + "lane_id": "lane-b", + "wp_ids": [ + "WP02" + ], + "write_scope": [ + "docs/admin/user-management.md" + ], + "predicted_surfaces": [ + "artifact-rendering", + "legacy-cleanup", + "workspace" + ], + "depends_on_lanes": [], + "parallel_group": 0 + }, + { + "lane_id": "lane-planning", + "wp_ids": [ + "WP03", + "WP04" + ], + "write_scope": [ + "docs/admin/blog-post-draft.md", + "docs/admin/coder-ddev-com/**" + ], + "predicted_surfaces": [ + "planning" + ], + "depends_on_lanes": [], + "parallel_group": 0 + } + ], + "computed_at": "2026-05-07T18:52:22.964347+00:00", + "computed_from": "dependency_graph+ownership", + "planning_artifact_wps": [ + "WP03", + "WP04" + ] +} diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/meta.json b/kitty-specs/github-org-gated-signup-01KR1P4G/meta.json new file mode 100644 index 0000000..5b00ae7 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/meta.json @@ -0,0 +1,12 @@ +{ + "created_at": "2026-05-07T17:00:31.394646+00:00", + "friendly_name": "GitHub Org-Gated Signup", + "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", + "mission_number": 1, + "mission_slug": "github-org-gated-signup-01KR1P4G", + "mission_type": "software-dev", + "slug": "github-org-gated-signup-01KR1P4G", + "source_description": "Restrict new account creation on coder.ddev.com and staging-coder.ddev.com to members of the ddev and coder-ddev-com GitHub organizations. References issues #64 and #54.", + "target_branch": "20260507_speckitty", + "vcs": "git" +} diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/mission-events.jsonl b/kitty-specs/github-org-gated-signup-01KR1P4G/mission-events.jsonl new file mode 100644 index 0000000..4e46c16 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/mission-events.jsonl @@ -0,0 +1,15 @@ +{"mission": "software-dev", "payload": {"action": "research", "agent": "claude", "decision_kind": "step", "mission_state": "discovery", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:47:44.275563+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "specify", "agent": "claude", "decision_kind": "step", "mission_state": "specify", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:49:45.261254+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "plan", "agent": "claude", "decision_kind": "step", "mission_state": "plan", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:50:03.176007+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "tasks-outline", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_outline", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:51:05.562761+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "tasks-packages", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_packages", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:51:46.676287+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "tasks-finalize", "agent": "claude", "decision_kind": "step", "mission_state": "tasks_finalize", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T18:52:15.848056+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP01"}, "timestamp": "2026-05-07T18:52:28.638408+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP02"}, "timestamp": "2026-05-07T18:59:00.206911+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP03"}, "timestamp": "2026-05-07T18:59:54.583177+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "implement", "agent": "claude", "decision_kind": "step", "mission_state": "implement", "result_input": "success", "wp_id": "WP04"}, "timestamp": "2026-05-07T19:01:36.970634+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "review", "agent": "claude", "decision_kind": "step", "mission_state": "review", "result_input": "success", "wp_id": "WP01"}, "timestamp": "2026-05-07T19:02:19.562296+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "review", "agent": "claude", "decision_kind": "step", "mission_state": "review", "result_input": "success", "wp_id": "WP02"}, "timestamp": "2026-05-07T19:03:11.234101+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "review", "agent": "claude", "decision_kind": "step", "mission_state": "review", "result_input": "success", "wp_id": "WP03"}, "timestamp": "2026-05-07T19:03:29.663020+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": "review", "agent": "claude", "decision_kind": "step", "mission_state": "review", "result_input": "success", "wp_id": "WP04"}, "timestamp": "2026-05-07T19:04:05.346776+00:00", "type": "MissionNextInvoked"} +{"mission": "software-dev", "payload": {"action": null, "agent": "claude", "decision_kind": "terminal", "mission_state": "done", "result_input": "success", "wp_id": null}, "timestamp": "2026-05-07T19:04:56.122284+00:00", "type": "MissionNextInvoked"} diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/plan.md b/kitty-specs/github-org-gated-signup-01KR1P4G/plan.md new file mode 100644 index 0000000..f01d47d --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/plan.md @@ -0,0 +1,131 @@ +# Implementation Plan: GitHub Org-Gated Signup + +**Branch**: `20260507_speckitty` | **Date**: 2026-05-07 | **Spec**: [spec.md](spec.md) + +**Branch contract**: Planning base `20260507_speckitty` → merge target `20260507_speckitty`. + +--- + +## Summary + +Restrict new account signups on coder.ddev.com and staging-coder.ddev.com to members of the `ddev` and `coder-ddev-com` GitHub organizations. The Coder server is deployed via the apt deb package and managed by systemd; its runtime configuration lives in `/etc/coder.d/coder.env` on the server host (not committed to this repo). The docs in this repo (`docs/admin/server-setup.md`, `docs/admin/user-management.md`) are the authoritative operator reference and will be updated to reflect the new configuration. Two separate GitHub OAuth Apps (one per environment) will be registered under the `ddev` GitHub org for credential isolation. The `coder-ddev-com` GitHub org will be created as the managed access list for non-ddev-org users. + +--- + +## Technical Context + +**Language/Version**: Markdown (documentation updates) +**Primary Dependencies**: Coder server env vars in `/etc/coder.d/coder.env` (managed on host, not in repo) +**Storage**: N/A +**Testing**: Manual scenario tests against staging-coder.ddev.com; existing BATS integration test suite +**Target Platform**: Ubuntu server running Coder via apt deb package + systemd +**Project Type**: Ops change + documentation update +**Performance Goals**: No increase in login round-trip time (under 10 seconds) +**Constraints**: Password auth must remain enabled; staging must be validated before production; OAuth credentials must not be committed to the repo + +--- + +## Charter Check + +- **All changes via PR**: ✓ — plan tracked in `kitty-specs/`, doc changes committed via PR on `20260507_speckitty` +- **Staging before production**: ✓ — explicit work package gate between staging validation and production rollout +- **No credentials in repo**: ✓ — `/etc/coder.d/coder.env` is a server-side file; client ID/secret documented as operator-supplied values only +- **Integration tests run against staging**: ✓ — staging validation WP includes manual scenario testing; BATS suite run post-config + +No charter violations. + +--- + +## Project Structure + +### Spec artifacts + +```text +kitty-specs/github-org-gated-signup-01KR1P4G/ +├── spec.md ✓ complete +├── plan.md ← this file +├── research.md ← Phase 0 output +└── tasks/ ← populated by /spec-kitty.tasks +``` + +### Source changes (repo root) + +```text +docs/admin/ +├── server-setup.md ← update: ALLOWED_ORGS full list, staging OAuth App section, coder-ddev-com docs, sponsor table, runbook +├── user-management.md ← update: add "Access Management" section +├── coder-ddev-com/ ← new: org README, access-requests repo README, issue template, sponsor notification +└── blog-post-draft.md ← new: ready-to-apply diff for ddev.com blog post +``` + +No Terraform, shell script, or Dockerfile changes required. + +--- + +## Phase 0: Research + +See [research.md](research.md). + +Key findings: all open questions resolved. No `[NEEDS CLARIFICATION]` markers remain. + +--- + +## Phase 1: Work Package Approach + +This feature decomposes into four work packages corresponding to the four documentation areas. + +### WP01 — Update `docs/admin/server-setup.md` + +- Update `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` to full 27-org list +- Add staging OAuth App sub-section (separate app, staging callback URL) +- Document `coder-ddev-com` org purpose and membership management +- Add sponsor org access table (company → GitHub slug mapping) +- Add "Adding a new sponsor org" runbook + +### WP02 — Update `docs/admin/user-management.md` + +- Add "Access Management" top-level section +- Document granting access via `coder-ddev-com` org membership +- Document pre-creating password exception accounts +- Note on private org membership; list initial `coder-ddev-com` members + +### WP03 — `coder-ddev-com` org content drafts (`docs/admin/coder-ddev-com/`) + +- Org profile README draft (`.github/profile/README.md` content for the org) +- `access-requests` repo README draft (how to open a request, what to expect) +- Access-request GitHub issue template draft +- Sponsor notification message template + +### WP04 — Blog post update draft (`docs/admin/blog-post-draft.md`) + +- Update "Log In with GitHub" section — no longer open signup +- Add access restriction paragraph and access paths (coder-ddev-com, access-requests link) +- Add sponsor org access benefit mention +- This is a draft diff for an operator to submit as a PR to `ddev/ddev.com` + +**WP04 depends on WP03** (needs access-requests repo URL/name). + +--- + +## Staging Validation Scenarios (must all pass before production) + +| # | Scenario | Expected | +| - | -------- | -------- | +| 1 | `ddev` org member signs in via GitHub | Account created, dashboard loads | +| 2 | `coder-ddev-com` org member signs in via GitHub | Account created, dashboard loads | +| 3 | Unauthorized GitHub user attempts sign-in | Error shown, no account created | +| 4 | Existing user (pre-existing account) logs in | Access unchanged | +| 5 | Exception account via password | Password login succeeds | + +--- + +## Ops Tasks (operator-executed, documented in runbook) + +These are documented in the deliverables but not automated by this repo: + +1. Create `coder-ddev-com` GitHub org; add initial members (`dougvann`, others when known) +2. Register staging OAuth App under `ddev` org with staging callback URL +3. Register production OAuth App under `ddev` org with production callback URL +4. Apply config to staging; validate all 5 scenarios above +5. Apply config to production after staging validation passes +6. Notify each sponsor org in `ALLOWED_ORGS` (use WP03 notification template) diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/research.md b/kitty-specs/github-org-gated-signup-01KR1P4G/research.md new file mode 100644 index 0000000..a8f089a --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/research.md @@ -0,0 +1,135 @@ +# Research: GitHub Org-Gated Signup + +## How Coder server configuration is applied + +**Decision**: Edit `/etc/coder.d/coder.env` on the server host, then `sudo systemctl restart coder`. + +**Rationale**: Coder is installed via the apt deb package, which installs a systemd unit that reads `/etc/coder.d/coder.env` at startup. This file is the canonical location for all runtime env vars. It is not committed to this repo — credentials and environment-specific values stay on the host. + +**Alternatives considered**: Terraform-managed env injection (not applicable here — Terraform manages workspace templates, not the Coder server itself). + +--- + +## One OAuth App vs. two for staging and production + +**Decision**: Two separate OAuth Apps — one for staging-coder.ddev.com, one for coder.ddev.com. + +**Rationale**: Separate apps mean staging credentials cannot be used against production. Secret rotation on one environment does not affect the other. Aligns with the charter's staging-first validation constraint (C-004). + +**Alternatives considered**: Single app with both callback URLs — simpler to manage but couples the two environments' credentials. + +--- + +## Custom OAuth App vs. Coder's default app + +**Decision**: Custom DDEV-owned GitHub OAuth App registered under the `ddev` org. + +**Rationale**: Coder's documentation explicitly recommends custom apps for production: "For production environments, we strongly recommend that you configure your own GitHub OAuth app to ensure that your data is not shared with Coder (the company)." The default app routes org membership data through Coder's infrastructure. + +**Alternatives considered**: Default Coder app — zero setup, but shares org data with Coder the company. + +--- + +## Private org membership and `read:org` scope + +**Decision**: Private org membership works with `read:org` scope. Users do not need to publicize their `ddev` or `coder-ddev-com` membership. + +**Rationale**: The `read:org` OAuth scope allows the app to read organization membership regardless of visibility setting. GitHub's API returns private membership when this scope is granted. This satisfies NFR-002. + +**Warning already documented in server-setup.md**: Do not enable Device Flow (`CODER_OAUTH2_GITHUB_DEVICE_FLOW=true`) alongside `ALLOWED_ORGS` — device flow does not request `read:org` and org membership checks fail with 403. + +--- + +## Required env vars summary + +```bash +# /etc/coder.d/coder.env additions (staging and production, separate client IDs/secrets) +CODER_OAUTH2_GITHUB_CLIENT_ID= +CODER_OAUTH2_GITHUB_CLIENT_SECRET= +CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS=true +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev,coder-ddev-com +# CODER_OAUTH2_GITHUB_DEVICE_FLOW must NOT be set to true +# CODER_DISABLE_PASSWORD_AUTH must NOT be set to true +``` + +--- + +## GitHub org for OAuth App ownership + +**Decision**: Register the OAuth Apps under the `ddev` GitHub organization. + +**Rationale**: Apps registered under an org show "by ddev" on the authorization screen instead of "by \". Institutional ownership means the app persists even if the registering individual leaves the org. Existing `docs/admin/server-setup.md` already documents this pattern. + +--- + +## What files in this repo change + +| File | Change | +| ---- | ------ | +| `docs/admin/server-setup.md` | Update `ALLOWED_ORGS` value, add staging OAuth App section, document `coder-ddev-com` org | +| `docs/admin/user-management.md` | Add "Access Management" section: how to add users to `coder-ddev-com`, how to pre-create password exception accounts | + +No Terraform, shell, or Dockerfile changes needed. + +--- + +## $100+/month sponsor GitHub org names + +**Finding**: The `ddev/sponsorship-data` `invoiced-sponsorships.jsonc` file records billing tiers and company names in comments but no `github_org` field. Slugs resolved by GitHub API lookup below. + +### Confirmed — add to `ALLOWED_ORGS` + +All featured sponsors on ddev.com are at the $100/month level. MacStadium and JetBrains are in-kind (not cash) sponsors and are excluded. The ddev.com sponsor link for Webikon points to the individual `claudiu-cristea`, so the org is excluded and the individual gets `coder-ddev-com` membership instead. + +| Company | Source | GitHub org slug | +| ------- | ------ | --------------- | +| Tag1 | invoiced | `tag1consulting` | +| Upsun | invoiced | `upsun` | +| Platform.sh (rebranded to Upsun) | invoiced | `platformsh` | +| Institute for Advanced Studies | invoiced | `Institute-for-Advanced-Studies` ⚠️ org exists but unverified — confirm with operator | +| CPS-IT | invoiced | `CPS-IT` | +| Redfin Solutions | invoiced + featured | `redfinsolutions` | +| Lullabot | invoiced | `Lullabot` | +| B13 | invoiced + featured | `b13` | +| Pixel & Tonic (Craft CMS) | invoiced + featured | `pixelandtonic` | +| Cambrico | invoiced + featured | `Cambrico` | +| Centarro | invoiced + featured | `centarro` | +| 8mylez | invoiced | `8mylez` | +| dkd Internet Service GmbH | GitHub Sponsors | `dkd` | +| Liip | GitHub Sponsors | `liip` | +| i-gelb GmbH | featured | `i-gelb` | +| Fame Helsinki | featured | `FameHelsinki` | +| Gizra | featured | `Gizra` | +| mobilistics GmbH | featured | `mobilistics` | +| OPTASY | featured | `OPTASY` | +| Passbolt | featured | `passbolt` | +| Værsågod | featured | `vaersaagod` | +| Affinity Bridge | featured | `affinitybridge` | +| Agiledrop | featured | `AGILEDROP` | +| NPO Applications GmbH | featured | `NPO-Applications-GmbH` | +| Aten Design Group | featured | `AtenDesignGroup` | + +### Individual/unresolved — add to `coder-ddev-com` org + +| Sponsor | Notes | +| ------- | ----- | +| `dougvann` | Individual $100/month GitHub Sponsor — confirmed via [ddev/ddev.com#626](https://github.com/ddev/ddev.com/pull/626) | +| claudiu-cristea (Webikon) | ddev.com sponsor link points to individual, not org | +| LetsTalk | No confirmed GitHub org (`lets-talk` is wrong org); add individual GitHub username when known | +| Amedick Sommer | No GitHub org found; add individual GitHub username when known | +| Pottkinder GmbH | No GitHub org found; add individual GitHub username when known | + +### Excluded + +| Sponsor | Reason | +| ------- | ------ | +| MacStadium | In-kind (hardware), not cash sponsor | +| JetBrains | In-kind (licenses), not cash sponsor | + +### Resulting `ALLOWED_ORGS` value (staging/production) + +```bash +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev,coder-ddev-com,tag1consulting,upsun,platformsh,Institute-for-Advanced-Studies,CPS-IT,redfinsolutions,Lullabot,b13,pixelandtonic,Cambrico,centarro,8mylez,dkd,liip,i-gelb,FameHelsinki,Gizra,mobilistics,OPTASY,passbolt,vaersaagod,affinitybridge,AGILEDROP,NPO-Applications-GmbH,AtenDesignGroup +# Webikon: sponsor link is individual claudiu-cristea — add to coder-ddev-com instead +# LetsTalk, Amedick Sommer, Pottkinder GmbH: no GitHub org — add individuals to coder-ddev-com when known +``` diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/research/evidence-log.csv b/kitty-specs/github-org-gated-signup-01KR1P4G/research/evidence-log.csv new file mode 100644 index 0000000..226045f --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/research/evidence-log.csv @@ -0,0 +1,23 @@ +# Evidence Log: Track all research findings with citations +# +# Column Definitions: +# - timestamp: When evidence was collected (ISO format: YYYY-MM-DDTHH:MM:SS) +# - source_type: journal | conference | book | web | preprint +# - citation: Full citation (BibTeX, APA, or Simple format) +# - key_finding: Main takeaway from this source (1-2 sentences) +# - confidence: high | medium | low (based on source quality and clarity) +# - notes: Additional context, caveats, or methodological notes +# +# Validation: Citations must be non-empty, source_type must be valid +# Format: BibTeX (@article{...}) or APA (Author (Year). Title.) recommended +timestamp,source_type,citation,key_finding,confidence,notes +2026-05-07T17:00:00,web,"Coder Docs. GitHub OAuth. https://coder.com/docs/admin/external-auth",CODER_OAUTH2_GITHUB_ALLOWED_ORGS restricts new signups to members of listed GitHub orgs.,high,Primary reference for env var names and behavior +2026-05-07T17:00:00,web,"Coder Docs. GitHub OAuth — Custom App. https://coder.com/docs/admin/external-auth","For production, Coder strongly recommends a custom GitHub OAuth App to avoid sharing org membership data with Coder the company.",high,Quoted directly in research.md +2026-05-07T17:00:00,web,"GitHub Docs. OAuth scopes. https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps",The read:org scope allows reading private org membership without requiring users to publicize it.,high,Satisfies NFR-002 +2026-05-07T17:00:00,web,"Coder Docs. Device Flow warning. https://coder.com/docs/admin/external-auth",Device flow (CODER_OAUTH2_GITHUB_DEVICE_FLOW=true) does not request read:org; enabling it alongside ALLOWED_ORGS causes 403 errors on org membership checks.,high,Already documented in server-setup.md; must remain in runbook +2026-05-07T17:00:00,web,"ddev/ddev.com PR #626. https://github.com/ddev/ddev.com/pull/626","Individual GitHub Sponsor dougvann confirmed at $100/month tier; no associated org, so add to coder-ddev-com.",medium,Used to resolve individual vs org ambiguity for dougvann +2026-05-07T17:00:00,web,"ddev/sponsorship-data invoiced-sponsorships.jsonc (repo internal)",invoiced-sponsorships.jsonc records billing tiers and company names but does not contain GitHub org slugs; manual mapping required.,high,Constraint C-007 in spec +2026-05-07T17:00:00,web,"GitHub org search and API lookups (manual, 2026-05-07)",Resolved 24 sponsor org slugs via GitHub org search. Institute-for-Advanced-Studies org exists but unverified — requires operator confirmation.,medium,See sponsor table in research.md for full mapping +2026-05-07T17:00:00,web,"ddev.com /support/sponsors page (featured sponsors list)",Featured sponsors on ddev.com are all at $100+/month. MacStadium and JetBrains are in-kind (excluded). Webikon link points to individual claudiu-cristea (excluded from ALLOWED_ORGS).,high,Cross-referenced with invoiced-sponsorships.jsonc +2026-05-07T17:00:00,web,"GitHub org ownership docs. https://docs.github.com/en/organizations",Registering OAuth App under an org shows 'by ' on auth screen and survives individual departures.,high,Satisfies FR-007; institutional ownership requirement +2026-05-07T17:00:00,web,"Coder apt install docs. https://coder.com/docs/install/server",Coder installed via apt reads /etc/coder.d/coder.env at systemd startup; no image rebuild needed to apply env changes.,high,Informs deployment model — config-only change diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/research/source-register.csv b/kitty-specs/github-org-gated-signup-01KR1P4G/research/source-register.csv new file mode 100644 index 0000000..b4a2d11 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/research/source-register.csv @@ -0,0 +1,25 @@ +# Source Register: Master list of all research sources +# +# Column Definitions: +# - source_id: Unique identifier (lowercase, no spaces, e.g., "smith2024") +# - citation: Full citation (BibTeX or APA format) +# - url: Direct link to source (DOI, arXiv, or web URL) +# - accessed_date: Date source was accessed (YYYY-MM-DD) +# - relevance: high | medium | low (to primary research question) +# - status: reviewed | pending | archived +# +# Usage: Add sources when discovered, update status as research progresses +# Validation: source_id must be unique +source_id,citation,url,accessed_date,relevance,status +coder-external-auth,"Coder Documentation. External Auth / GitHub OAuth.",https://coder.com/docs/admin/external-auth,2026-05-07,high,reviewed +coder-custom-oauth-app,"Coder Documentation. Custom GitHub OAuth App recommendation.",https://coder.com/docs/admin/external-auth,2026-05-07,high,reviewed +github-oauth-scopes,"GitHub Documentation. Scopes for OAuth apps.",https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps,2026-05-07,high,reviewed +coder-device-flow,"Coder Documentation. Device Flow warning for ALLOWED_ORGS.",https://coder.com/docs/admin/external-auth,2026-05-07,high,reviewed +coder-install-server,"Coder Documentation. Server installation (apt, systemd, coder.env).",https://coder.com/docs/install/server,2026-05-07,high,reviewed +ddev-sponsorship-data,"DDEV internal. invoiced-sponsorships.jsonc (ddev/sponsorship-data repo).",(internal repo),2026-05-07,high,reviewed +ddev-ddev-com-sponsors,"DDEV website. Featured sponsors page. https://ddev.com/support/sponsors",https://ddev.com/support/sponsors,2026-05-07,high,reviewed +ddev-ddev-com-pr626,"ddev/ddev.com PR #626. Sponsor listing confirming dougvann as individual $100/month.",https://github.com/ddev/ddev.com/pull/626,2026-05-07,medium,reviewed +github-org-search,"GitHub org search (manual lookups, 2026-05-07). Resolved sponsor company names to org slugs.",(manual lookup),2026-05-07,medium,reviewed +github-issue-64,"ddev/coder-ddev issue #64. User creation and authentication control.",https://github.com/ddev/coder-ddev/issues/64,2026-05-07,high,reviewed +github-issue-54,"ddev/coder-ddev issue #54. Offer additional login options.",https://github.com/ddev/coder-ddev/issues/54,2026-05-07,medium,reviewed +github-org-ownership,"GitHub Documentation. About organizations / transferring ownership.",https://docs.github.com/en/organizations,2026-05-07,medium,reviewed diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/spec.md b/kitty-specs/github-org-gated-signup-01KR1P4G/spec.md new file mode 100644 index 0000000..96a8408 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/spec.md @@ -0,0 +1,124 @@ +# GitHub Org-Gated Signup + +## Summary + +Currently, anyone with a GitHub account can create a Coder account on coder.ddev.com and spin up unlimited workspaces. This is unsustainable and represents a direct cost and security risk to the service. This feature restricts new account signups to members of two GitHub organizations — the existing `ddev` org and a new `coder-ddev-com` org created specifically as a managed access list — while keeping existing user accounts and password authentication intact. + +**Related issues:** [#64 User creation and authentication control](https://github.com/ddev/coder-ddev/issues/64), [#54 Offer additional login options](https://github.com/ddev/coder-ddev/issues/54) + +--- + +## Goals + +- Prevent arbitrary GitHub users from self-registering on coder.ddev.com or staging-coder.ddev.com. +- Allow all members of the `ddev` GitHub org to sign in and create workspaces. +- Allow all members of the `coder-ddev-com` GitHub org to sign in and create workspaces. +- Preserve access for users who already have accounts on coder.ddev.com. +- Maintain an emergency escape hatch: manually-provisioned accounts using password authentication must remain usable. +- Retain full DDEV ownership of OAuth credentials (no data sharing with Coder the company). + +## Non-Goals + +- No password authentication is added for new users. The existing password auth path is preserved only for pre-created exception accounts. +- No OIDC, GitLab, or other providers are added as part of this change. +- No changes to workspace templates, Docker images, or resource limits. + +--- + +## User Scenarios & Testing + +**Scenario 1 — ddev org member signs up (happy path):** +A developer who is a public member of the `ddev` GitHub org visits coder.ddev.com, clicks "Sign in with GitHub," authorizes the OAuth app, and lands on the Coder dashboard. Account is created automatically. + +**Scenario 2 — coder-ddev-com org member signs up:** +A developer who has been added to the `coder-ddev-com` GitHub org visits coder.ddev.com, clicks "Sign in with GitHub," and successfully creates an account. + +**Scenario 3 — unauthorized GitHub user is rejected:** +A GitHub user who is not a member of either `ddev` or `coder-ddev-com` attempts to sign in. They see an error message indicating they are not authorized. No account is created. + +**Scenario 4 — existing user retains access:** +A user who already has a coder.ddev.com account (created before this change) logs in via GitHub OAuth or password auth and accesses their workspaces without disruption. + +**Scenario 5 — exception account via password:** +An admin pre-creates a user account with a password credential. That user logs in without GitHub OAuth. This path continues to work because `CODER_DISABLE_PASSWORD_AUTH` is never set to `true`. + +**Scenario 6 — staging parity:** +The same org-restriction configuration is applied to staging-coder.ddev.com, so staging behavior accurately reflects production before promotion. + +--- + +## Functional Requirements + +| ID | Requirement | Status | +| ---- | ------------- | -------- | +| FR-001 | New GitHub OAuth signups are restricted to members of the `ddev` GitHub org and the `coder-ddev-com` GitHub org. Users outside both orgs cannot create accounts. | Approved | +| FR-002 | The `coder-ddev-com` GitHub org exists and can have members added to grant access without requiring `ddev` org membership. | Approved | +| FR-003 | Existing user accounts on coder.ddev.com remain active and accessible after the restriction is applied. | Approved | +| FR-004 | Password-based login remains available for manually-provisioned accounts. Password auth is never globally disabled. | Approved | +| FR-005 | Both staging-coder.ddev.com and coder.ddev.com have identical org-restriction configuration. | Approved | +| FR-006 | A custom DDEV-owned GitHub OAuth App is registered and used for authentication, replacing the default Coder-provided app. | Approved | +| FR-007 | The OAuth App is registered under the `ddev` GitHub organization so credentials are DDEV-controlled, not held by an individual account. | Approved | +| FR-008 | Authorized users from both orgs can create and use workspaces on both environments. | Approved | +| FR-009 | An operator runbook documents how to add a user to the `coder-ddev-com` org and how to pre-create exception accounts with password credentials. | Approved | +| FR-010 | The `coder-ddev-com` GitHub org has a README (in its `.github` or dedicated `about` repo) explaining the org's purpose, who it is for, and how access works. | Approved | +| FR-011 | A public repository in the `coder-ddev-com` org provides an issue tracker where prospective users can request access by opening a GitHub issue. | Approved | +| FR-012 | The ddev.com blog post announcing coder.ddev.com is updated to explain that signups are now restricted, how the `coder-ddev-com` org works, and where to open an access request. | Approved | +| FR-013 | GitHub organizations that sponsor DDEV at $100+/month (via GitHub Sponsors or invoiced billing) are eligible for access: all members of those orgs can sign in to coder.ddev.com without individual `coder-ddev-com` membership. | Approved | +| FR-014 | A runbook documents how to identify the GitHub org name for a new $100+ sponsor and add it to the allowed-orgs list on both environments. | Approved | +| FR-015 | Each sponsor org that has been granted access is notified about the benefit — what access they have, how to use it, and who to contact if they need help. | Approved | + +--- + +## Non-Functional Requirements + +| ID | Requirement | Threshold | Status | +| ---- | ------------- | ----------- | -------- | +| NFR-001 | Authorized users experience no increase in login time after the change. | Sign-in round-trip completes within the same time as before (under 10 seconds on a normal connection). | Approved | +| NFR-002 | The org membership check does not require users to make their org membership public. | Private `ddev` or `coder-ddev-com` org membership must be sufficient for login. | Approved | +| NFR-003 | Applying the configuration change causes zero downtime for existing active sessions. | No active workspace sessions are interrupted during config rollout. | Approved | + +--- + +## Constraints + +| ID | Constraint | Status | +| ---- | ---------- | -------- | +| C-001 | `CODER_DISABLE_PASSWORD_AUTH` must never be set to `true`. Password auth must remain available for exception accounts. | Approved | +| C-002 | The `coder-ddev-com` GitHub org must be a real GitHub organization (not a personal account) so membership can be managed by multiple org owners. | Approved | +| C-003 | The custom GitHub OAuth App must request at minimum: `read:user`, `user:email`, and `read:org` scopes. The `read:org` scope is required for Coder to verify org membership. | Approved | +| C-004 | The change must be applied to staging first and validated before applying to production. | Approved | +| C-005 | OAuth App credentials (client ID and client secret) must be stored as server-level secrets, not committed to the repository. | Approved | +| C-006 | Sponsor orgs are added to `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` directly (not proxied via `coder-ddev-com` membership), so all members of a sponsor org benefit automatically without individual enrollment. | Approved | +| C-007 | The `invoiced-sponsorships.jsonc` data file does not contain GitHub org names; a manual or scripted mapping from company name to GitHub org name is required before any sponsor org can be added. | Approved | + +--- + +## Success Criteria + +1. A GitHub user who is not in `ddev` or `coder-ddev-com` cannot create an account on coder.ddev.com. +2. All current members of the `ddev` org can sign in to coder.ddev.com without any manual step beyond GitHub OAuth. +3. An admin can grant access to a new non-ddev-org user by adding them to `coder-ddev-com`, with no Coder server change required. +4. At least one pre-existing user account continues to function after the rollout (no accounts locked out). +5. Staging-coder.ddev.com and coder.ddev.com enforce the same restriction after both are reconfigured. +6. A prospective user who is not in `ddev` or `coder-ddev-com` can find the access-request issue tracker without assistance. +7. The ddev.com blog post no longer implies open signup; it accurately describes the access model and links to the access-request repo. +8. Members of at least one confirmed $100+/month sponsor org can sign in to coder.ddev.com without being explicitly added to `coder-ddev-com`. +9. Each sponsor org listed in `ALLOWED_ORGS` has been notified of the access benefit before or shortly after the production rollout. + +--- + +## Assumptions + +- The `coder-ddev-com` GitHub organization does not yet exist and must be created before configuration is applied. +- The current Coder server deployment on both environments exposes environment variables that can be updated without rebuilding images. +- Coder's org membership check works with private org membership when the `read:org` scope is granted — users do not need to publicize their org membership. +- The GitHub OAuth App will be registered under the `ddev` org (not a personal account), so ownership is institutional. + +--- + +## Out of Scope + +- Automating `coder-ddev-com` membership management (e.g., via a bot or form). +- Removing or migrating existing user accounts. +- Implementing GitLab, OIDC, or any other auth provider. +- Rate limiting, abuse prevention, or workspace quota enforcement (separate concern from issue #64's unlimited workspace problem). diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/status.events.jsonl b/kitty-specs/github-org-gated-signup-01KR1P4G/status.events.jsonl new file mode 100644 index 0000000..df5e11a --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/status.events.jsonl @@ -0,0 +1,24 @@ +{"actor": "finalize-tasks", "at": "2026-05-07T18:04:47.165313+00:00", "event_id": "01KR1ST5NXMPC2VG8WH5J2C72X", "evidence": null, "execution_mode": "worktree", "force": true, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "canonical bootstrap", "review_ref": null, "to_lane": "planned", "wp_id": "WP01"} +{"actor": "finalize-tasks", "at": "2026-05-07T18:04:47.235255+00:00", "event_id": "01KR1ST5R35T8JA744EC0RMJEN", "evidence": null, "execution_mode": "worktree", "force": true, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "canonical bootstrap", "review_ref": null, "to_lane": "planned", "wp_id": "WP02"} +{"actor": "finalize-tasks", "at": "2026-05-07T18:04:47.236342+00:00", "event_id": "01KR1ST5R4262WM8FB01TBP1QA", "evidence": null, "execution_mode": "worktree", "force": true, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "canonical bootstrap", "review_ref": null, "to_lane": "planned", "wp_id": "WP03"} +{"actor": "finalize-tasks", "at": "2026-05-07T18:04:47.237340+00:00", "event_id": "01KR1ST5R57BNZQ55XMMB3WG64", "evidence": null, "execution_mode": "worktree", "force": true, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "canonical bootstrap", "review_ref": null, "to_lane": "planned", "wp_id": "WP04"} +{"actor": "implement-command", "at": "2026-05-07T18:56:26.259833+00:00", "event_id": "01KR1WRR4K4VCB3EHN49FWMNCD", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "claimed", "wp_id": "WP01"} +{"actor": "implement-command", "at": "2026-05-07T18:56:26.350632+00:00", "event_id": "01KR1WRR7EDJNJ3QKMNGWQKN4C", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "claimed", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "in_progress", "wp_id": "WP01"} +{"actor": "user", "at": "2026-05-07T18:58:51.454594+00:00", "event_id": "01KR1WX5XY3JKX9XH9GTJHY6ER", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "in_progress", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Ready for review: server-setup.md updated with full 27-org ALLOWED_ORGS, staging OAuth App section, coder-ddev-com management docs, sponsor table, and runbook", "review_ref": null, "to_lane": "for_review", "wp_id": "WP01"} +{"actor": "implement-command", "at": "2026-05-07T18:59:05.948699+00:00", "event_id": "01KR1WXM2WAQPBQ7KP0TF1DCE8", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "claimed", "wp_id": "WP02"} +{"actor": "implement-command", "at": "2026-05-07T18:59:06.039477+00:00", "event_id": "01KR1WXM5QWPM67M2FQ6W2RH3H", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "claimed", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "in_progress", "wp_id": "WP02"} +{"actor": "user", "at": "2026-05-07T18:59:50.080669+00:00", "event_id": "01KR1WYZ60DJ46PDSRTMVG7X1P", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "in_progress", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Ready for review: Access Management section added to user-management.md", "review_ref": null, "to_lane": "for_review", "wp_id": "WP02"} +{"actor": "claude", "at": "2026-05-07T19:00:15.480301+00:00", "event_id": "01KR1WZQZR170YS0MTBZX359X5", "evidence": null, "execution_mode": "direct_repo", "force": false, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "claimed", "wp_id": "WP03"} +{"actor": "claude", "at": "2026-05-07T19:00:15.572465+00:00", "event_id": "01KR1WZR2MZWT63YRJBDEXR4GY", "evidence": null, "execution_mode": "direct_repo", "force": false, "from_lane": "claimed", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "in_progress", "wp_id": "WP03"} +{"actor": "user", "at": "2026-05-07T19:01:31.269163+00:00", "event_id": "01KR1X2205N6K31W5W6MSDE0QE", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "in_progress", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Ready for review: org-profile-README, access-requests-README, issue template, sponsor notification all created in docs/admin/coder-ddev-com/", "review_ref": null, "to_lane": "for_review", "wp_id": "WP03"} +{"actor": "claude", "at": "2026-05-07T19:01:42.007379+00:00", "event_id": "01KR1X2CFQ6ANG2YB9ZTDA44DT", "evidence": null, "execution_mode": "direct_repo", "force": false, "from_lane": "planned", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "claimed", "wp_id": "WP04"} +{"actor": "claude", "at": "2026-05-07T19:01:42.100559+00:00", "event_id": "01KR1X2CJMGCSN411BGXADEVA2", "evidence": null, "execution_mode": "direct_repo", "force": false, "from_lane": "claimed", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": null, "review_ref": null, "to_lane": "in_progress", "wp_id": "WP04"} +{"actor": "user", "at": "2026-05-07T19:02:14.030626+00:00", "event_id": "01KR1X3BREWGYZD5VWHTCMMTEM", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "in_progress", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Ready for review: blog-post-draft.md created with 3 sections", "review_ref": null, "to_lane": "for_review", "wp_id": "WP04"} +{"actor": "user", "at": "2026-05-07T19:03:02.971035+00:00", "event_id": "01KR1X4VHVTRKEBN8QNC7FJSWK", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "for_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: all subtasks complete and verified", "review_ref": null, "to_lane": "in_review", "wp_id": "WP01"} +{"actor": "user", "at": "2026-05-07T19:03:03.055895+00:00", "event_id": "01KR1X4VMFF2143XS72GZD5KW6", "evidence": {"review": {"reference": "Review passed: all subtasks complete and verified", "reviewer": "Randy Fay", "verdict": "approved"}}, "execution_mode": "worktree", "force": false, "from_lane": "in_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: all subtasks complete and verified", "review_ref": null, "to_lane": "approved", "wp_id": "WP01"} +{"actor": "user", "at": "2026-05-07T19:03:23.980400+00:00", "event_id": "01KR1X5G2CA5MEABZV1NN1KSNH", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "for_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: Access Management section complete with all 4 subtask topics", "review_ref": null, "to_lane": "in_review", "wp_id": "WP02"} +{"actor": "user", "at": "2026-05-07T19:03:24.069266+00:00", "event_id": "01KR1X5G55FJ8762EW7PCQVRZ4", "evidence": {"review": {"reference": "Review passed: Access Management section complete with all 4 subtask topics", "reviewer": "Randy Fay", "verdict": "approved"}}, "execution_mode": "worktree", "force": false, "from_lane": "in_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: Access Management section complete with all 4 subtask topics", "review_ref": null, "to_lane": "approved", "wp_id": "WP02"} +{"actor": "user", "at": "2026-05-07T19:04:01.068809+00:00", "event_id": "01KR1X6M9CQZQKB10R2G973FQ1", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "for_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: all 4 files created with operator notes. YAML template structure verified.", "review_ref": null, "to_lane": "in_review", "wp_id": "WP03"} +{"actor": "user", "at": "2026-05-07T19:04:01.160896+00:00", "event_id": "01KR1X6MC85P9D1GT7TKMYXNS9", "evidence": {"review": {"reference": "Review passed: all 4 files created with operator notes. YAML template structure verified.", "reviewer": "Randy Fay", "verdict": "approved"}}, "execution_mode": "worktree", "force": false, "from_lane": "in_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: all 4 files created with operator notes. YAML template structure verified.", "review_ref": null, "to_lane": "approved", "wp_id": "WP03"} +{"actor": "user", "at": "2026-05-07T19:04:15.031093+00:00", "event_id": "01KR1X71XQJK42VZ3CKAC0B85G", "evidence": null, "execution_mode": "worktree", "force": false, "from_lane": "for_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: blog-post-draft.md has all 3 required sections and correct access-requests link", "review_ref": null, "to_lane": "in_review", "wp_id": "WP04"} +{"actor": "user", "at": "2026-05-07T19:04:15.121111+00:00", "event_id": "01KR1X720HBERH8WV858JGT87Z", "evidence": {"review": {"reference": "Review passed: blog-post-draft.md has all 3 required sections and correct access-requests link", "reviewer": "Randy Fay", "verdict": "approved"}}, "execution_mode": "worktree", "force": false, "from_lane": "in_review", "mission_id": "01KR1P4G5AJEBQMAZH1DKNQZHV", "mission_slug": "github-org-gated-signup-01KR1P4G", "policy_metadata": null, "reason": "Review passed: blog-post-draft.md has all 3 required sections and correct access-requests link", "review_ref": null, "to_lane": "approved", "wp_id": "WP04"} diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/status.json b/kitty-specs/github-org-gated-signup-01KR1P4G/status.json new file mode 100644 index 0000000..a32e80f --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/status.json @@ -0,0 +1,49 @@ +{ + "event_count": 24, + "last_event_id": "01KR1X720HBERH8WV858JGT87Z", + "materialized_at": "2026-05-07T19:04:15.121111+00:00", + "mission_number": "1", + "mission_slug": "github-org-gated-signup-01KR1P4G", + "mission_type": "software-dev", + "summary": { + "approved": 4, + "blocked": 0, + "canceled": 0, + "claimed": 0, + "done": 0, + "for_review": 0, + "in_progress": 0, + "in_review": 0, + "planned": 0 + }, + "work_packages": { + "WP01": { + "actor": "user", + "force_count": 1, + "lane": "approved", + "last_event_id": "01KR1X4VMFF2143XS72GZD5KW6", + "last_transition_at": "2026-05-07T19:03:03.055895+00:00" + }, + "WP02": { + "actor": "user", + "force_count": 1, + "lane": "approved", + "last_event_id": "01KR1X5G55FJ8762EW7PCQVRZ4", + "last_transition_at": "2026-05-07T19:03:24.069266+00:00" + }, + "WP03": { + "actor": "user", + "force_count": 1, + "lane": "approved", + "last_event_id": "01KR1X6MC85P9D1GT7TKMYXNS9", + "last_transition_at": "2026-05-07T19:04:01.160896+00:00" + }, + "WP04": { + "actor": "user", + "force_count": 1, + "lane": "approved", + "last_event_id": "01KR1X720HBERH8WV858JGT87Z", + "last_transition_at": "2026-05-07T19:04:15.121111+00:00" + } + } +} diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks.md new file mode 100644 index 0000000..214b7d3 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks.md @@ -0,0 +1,45 @@ +# Work Packages: github-org-gated-signup-01KR1P4G + +_Generated by finalize-tasks from wps.yaml. Do not edit directly._ + +--- + +## Work Package WP01: Update server-setup.md for Org-Gated Auth + +**Dependencies**: None +**Requirement Refs**: FR-001, FR-005, FR-006, FR-007, FR-013, FR-014, C-005, C-006, C-007 +**Owned Files**: docs/admin/server-setup.md +**Subtasks**: T001, T002, T003, T004, T005 +**Prompt**: `tasks/WP01-server-setup-update.md` + +--- + +## Work Package WP02: Update user-management.md with Access Runbook + +**Dependencies**: None +**Requirement Refs**: FR-002, FR-003, FR-004, FR-009, NFR-002, C-001 +**Owned Files**: docs/admin/user-management.md +**Subtasks**: T006, T007, T008, T009 +**Prompt**: `tasks/WP02-user-management-access-runbook.md` + +--- + +## Work Package WP03: coder-ddev-com Org Content Drafts + +**Dependencies**: None +**Requirement Refs**: FR-010, FR-011, FR-015, C-002 +**Owned Files**: docs/admin/coder-ddev-com/** +**Subtasks**: T010, T011, T012, T013 +**Prompt**: `tasks/WP03-coder-ddev-com-org-content.md` + +--- + +## Work Package WP04: Blog Post Update Draft + +**Dependencies**: WP03 +**Requirement Refs**: FR-012, FR-008 +**Owned Files**: docs/admin/blog-post-draft.md +**Subtasks**: T014, T015, T016 +**Prompt**: `tasks/WP04-blog-post-draft.md` + +--- diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/.gitkeep b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/README.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/README.md new file mode 100644 index 0000000..5757bd0 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/README.md @@ -0,0 +1,64 @@ +# Tasks Directory + +This directory contains work package (WP) prompt files. + +## Directory Structure (v0.9.0+) + +``` +tasks/ +├── WP01-setup-infrastructure.md +├── WP02-user-authentication.md +├── WP03-api-endpoints.md +└── README.md +``` + +All WP files are stored flat in `tasks/`. Status is tracked in `status.events.jsonl`, not in WP frontmatter. + +## Work Package File Format + +Each WP file **MUST** use YAML frontmatter: + +```yaml +--- +work_package_id: "WP01" +title: "Work Package Title" +dependencies: [] +planning_base_branch: "20260507_speckitty" +merge_target_branch: "20260507_speckitty" +branch_strategy: "Planning artifacts were generated on 20260507_speckitty; completed changes must merge back into 20260507_speckitty." +subtasks: + - "T001" + - "T002" +phase: "Phase 1 - Setup" +assignee: "" +agent: "" +shell_pid: "" +history: + - timestamp: "2025-01-01T00:00:00Z" + agent: "system" + action: "Prompt generated via /spec-kitty.tasks" +--- + +# Work Package Prompt: WP01 – Work Package Title + +[Content follows...] +``` + +## Status Tracking + +Status is tracked via the canonical event log (`status.events.jsonl`), not in WP frontmatter. +Use `spec-kitty agent tasks move-task` to change WP status: + +```bash +spec-kitty agent tasks move-task --to +``` + +Example: +```bash +spec-kitty agent tasks move-task WP01 --to doing +``` + +## File Naming + +- Format: `WP01-kebab-case-slug.md` +- Examples: `WP01-setup-infrastructure.md`, `WP02-user-auth.md` diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP01-server-setup-update.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP01-server-setup-update.md new file mode 100644 index 0000000..9bc8c23 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP01-server-setup-update.md @@ -0,0 +1,277 @@ +--- +work_package_id: WP01 +title: Update server-setup.md for Org-Gated Auth +dependencies: [] +requirement_refs: +- C-003 +- C-004 +- C-005 +- C-006 +- C-007 +- FR-001 +- FR-005 +- FR-006 +- FR-007 +- FR-008 +- FR-009 +- FR-013 +- FR-014 +planning_base_branch: 20260507_speckitty +merge_target_branch: 20260507_speckitty +branch_strategy: Planning artifacts for this feature were generated on 20260507_speckitty. During /spec-kitty.implement this WP may branch from a dependency-specific base, but completed changes must merge back into 20260507_speckitty unless the human explicitly redirects the landing branch. +base_branch: kitty/mission-github-org-gated-signup-01KR1P4G +base_commit: e125c1d60f5ea0f1c84475d06a5947d15b201b60 +created_at: '2026-05-07T18:56:26.245758+00:00' +subtasks: +- T001 +- T002 +- T003 +- T004 +- T005 +shell_pid: "25745" +agent: "claude" +history: +- date: '2026-05-07' + event: created +authoritative_surface: docs/admin/server-setup.md +execution_mode: code_change +owned_files: +- docs/admin/server-setup.md +tags: [] +--- + +# WP01 — Update server-setup.md for Org-Gated Auth + +## Branch Strategy + +- **Planning base**: `20260507_speckitty` +- **Merge target**: `20260507_speckitty` +- Implement directly on `20260507_speckitty`. Run `spec-kitty agent action implement WP01 --agent claude`. + +## Objective + +Update `docs/admin/server-setup.md` to be the authoritative operator reference for the new GitHub org-gated authentication model. The current doc covers only a single `ddev`-org restriction; it needs to reflect: +- The full 27-org `ALLOWED_ORGS` list (sponsors + ddev + coder-ddev-com) +- Two separate OAuth Apps (staging vs. production) +- The purpose and management of the `coder-ddev-com` org +- The sponsor org access policy with the full mapping table +- A runbook for adding a new sponsor org + +## Context + +The Coder server is deployed via apt deb package on Ubuntu, managed by systemd. Runtime config lives in `/etc/coder.d/coder.env` on the host — not committed to this repo. `docs/admin/server-setup.md` is the operator's guide for setting up that file. + +The current GitHub OAuth section (around line 660) shows only: +```bash +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev +``` + +This needs to become the full list, and the surrounding documentation needs to explain why so many orgs appear and how to manage them. + +**Read the current file before editing**: `docs/admin/server-setup.md`. The GitHub OAuth section spans approximately lines 660–710. All changes are within that section unless adding a new sub-section. + +## Subtask T001 — Update `ALLOWED_ORGS` to Full 27-Org List + +**Purpose**: Change the example env var block to include all approved orgs. + +**Location**: `docs/admin/server-setup.md`, inside the env var code block in "Step 2: Add to `/etc/coder.d/coder.env`". + +**Current value**: +```bash +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev +``` + +**New value**: +```bash +# Access control: ddev org members, coder-ddev-com managed list, and $100+/month sponsor orgs +CODER_OAUTH2_GITHUB_ALLOWED_ORGS=ddev,coder-ddev-com,tag1consulting,upsun,platformsh,Institute-for-Advanced-Studies,CPS-IT,redfinsolutions,Lullabot,b13,pixelandtonic,Cambrico,centarro,8mylez,dkd,liip,i-gelb,FameHelsinki,Gizra,mobilistics,OPTASY,passbolt,vaersaagod,affinitybridge,AGILEDROP,NPO-Applications-GmbH,AtenDesignGroup +``` + +**Note**: `Institute-for-Advanced-Studies` is included but flagged for operator verification — the GitHub org exists but has no public name/description confirming identity. Add an inline comment noting this. + +**Validation**: +- [ ] The code block compiles as valid bash (no stray characters) +- [ ] All 27 org slugs are present (count them) +- [ ] The comment above the var explains the three access tiers + +--- + +## Subtask T002 — Add Staging OAuth App Sub-section + +**Purpose**: The current doc only documents the production OAuth App. Operators need to know they must register a *separate* app for staging, with a different callback URL, before testing on staging-coder.ddev.com. + +**Location**: Add a new sub-section immediately after the "Create a GitHub OAuth App" step (currently "Step 1"), before or as part of the env var step. + +**Content to add** (adapt prose as needed, keep the tone of the existing doc): + +```markdown +#### Two OAuth Apps: staging and production + +Register **two separate OAuth Apps** — one for staging, one for production. Separate apps isolate credentials between environments so that a staging secret compromise cannot affect production, and secret rotation on one environment does not require touching the other. + +**Staging app settings:** +- **Application name**: `Coder (staging-coder.ddev.com)` +- **Homepage URL**: `https://staging-coder.ddev.com` +- **Authorization callback URL**: `https://staging-coder.ddev.com/api/v2/users/oauth2/github/callback` +- **Enable Device Flow**: leave unchecked + +**Production app settings** (unchanged from above): +- **Application name**: `Coder (coder.ddev.com)` +- **Homepage URL**: `https://coder.ddev.com` +- **Authorization callback URL**: `https://coder.ddev.com/api/v2/users/oauth2/github/callback` +- **Enable Device Flow**: leave unchecked + +Use the staging app's Client ID and Client Secret in `/etc/coder.d/coder.env` on staging-coder.ddev.com, and the production app's credentials on coder.ddev.com. +``` + +**Validation**: +- [ ] Both callback URLs are present and correct +- [ ] Device Flow warning is clear +- [ ] Staging section appears before the env var block it feeds into + +--- + +## Subtask T003 — Document `coder-ddev-com` Org Purpose + +**Purpose**: Explain to operators what `coder-ddev-com` is for — the managed access list for individuals who are not members of the `ddev` org or a sponsor org. + +**Location**: Add a new sub-section after the env var block, titled "### Managing individual access via `coder-ddev-com`". + +**Content to add**: + +```markdown +### Managing individual access via `coder-ddev-com` + +The `coder-ddev-com` GitHub organization is the managed access list for individuals who do not belong to the `ddev` org or one of the sponsor orgs. Adding someone to `coder-ddev-com` grants them signup access to coder.ddev.com without requiring a Coder server restart. + +**To grant access to an individual:** +1. Go to [github.com/coder-ddev-com](https://github.com/coder-ddev-com) → **People** → **Invite member** +2. Enter the person's GitHub username and send the invitation +3. Once they accept, they can log in to coder.ddev.com via GitHub OAuth + +**Initial members** (add these when creating the org): +- `dougvann` — individual $100/month GitHub Sponsor +- `claudiu-cristea` — Webikon sponsor (linked as individual on ddev.com) +- Add LetsTalk, Amedick Sommer, and Pottkinder GmbH contacts when their GitHub usernames are confirmed + +**Note on private membership**: Users do not need to make their `coder-ddev-com` membership public. Private membership is sufficient — the `read:org` OAuth scope allows Coder to verify membership regardless of visibility setting. +``` + +**Validation**: +- [ ] The "initial members" list matches research.md +- [ ] The privacy note is present +- [ ] The section is placed logically after the env var block + +--- + +## Subtask T004 — Add Sponsor Org Access Table + +**Purpose**: Document the full list of approved sponsor org slugs so operators know what's in `ALLOWED_ORGS` and why. + +**Location**: Add a sub-section "### Sponsor org access policy" after the `coder-ddev-com` section from T003. + +**Content to add**: + +```markdown +### Sponsor org access policy + +All $100+/month DDEV sponsors receive access as an org-level benefit: every member of a sponsor's GitHub org can sign in to coder.ddev.com without individual enrollment. MacStadium and JetBrains are excluded (in-kind, not cash sponsors). + +| Company | GitHub org | Source | +| ------- | ---------- | ------ | +| Tag1 | `tag1consulting` | invoiced | +| Upsun | `upsun` | invoiced | +| Platform.sh (Upsun predecessor) | `platformsh` | invoiced | +| Institute for Advanced Studies | `Institute-for-Advanced-Studies` ⚠️ | invoiced | +| CPS-IT | `CPS-IT` | invoiced | +| Redfin Solutions | `redfinsolutions` | invoiced + featured | +| Lullabot | `Lullabot` | invoiced | +| B13 | `b13` | invoiced + featured | +| Pixel & Tonic (Craft CMS) | `pixelandtonic` | invoiced + featured | +| Cambrico | `Cambrico` | invoiced + featured | +| Centarro | `centarro` | invoiced + featured | +| 8mylez | `8mylez` | invoiced | +| dkd Internet Service GmbH | `dkd` | GitHub Sponsors | +| Liip | `liip` | GitHub Sponsors | +| i-gelb GmbH | `i-gelb` | featured | +| Fame Helsinki | `FameHelsinki` | featured | +| Gizra | `Gizra` | featured | +| mobilistics GmbH | `mobilistics` | featured | +| OPTASY | `OPTASY` | featured | +| Passbolt | `passbolt` | featured | +| Værsågod | `vaersaagod` | featured | +| Affinity Bridge | `affinitybridge` | featured | +| Agiledrop | `AGILEDROP` | featured | +| NPO Applications GmbH | `NPO-Applications-GmbH` | featured | +| Aten Design Group | `AtenDesignGroup` | featured | + +⚠️ `Institute-for-Advanced-Studies` — GitHub org exists but has no public name/description. Confirm with operator before deployment that this is the correct org. + +Sponsors with no GitHub org (access via `coder-ddev-com` individual membership instead): LetsTalk, Amedick Sommer, Pottkinder GmbH. +``` + +**Validation**: +- [ ] Table has 25 rows (all confirmed sponsor orgs) +- [ ] IAS warning note is present +- [ ] Excluded and unresolved sponsors are noted + +--- + +## Subtask T005 — Add "Adding a New Sponsor Org" Runbook + +**Purpose**: When a new organization becomes a $100+/month sponsor, an operator needs to know exactly how to grant them access. This runbook is the process. + +**Location**: Add after the sponsor org table, as "#### Adding a new sponsor org". + +**Content to add**: + +```markdown +#### Adding a new sponsor org + +When a new organization reaches the $100+/month sponsorship level: + +1. **Find their GitHub org slug**: Check the sponsor's GitHub profile or ask them directly. Verify with `gh api orgs/ --jq '.login'` — if the command returns the slug, the org exists. + +2. **Add the slug to `ALLOWED_ORGS`** on both staging and production: + ```bash + # On the server, edit /etc/coder.d/coder.env + # Append the new slug to CODER_OAUTH2_GITHUB_ALLOWED_ORGS (comma-separated, no spaces) + sudo systemctl restart coder + ``` + +3. **Test on staging first**: Ask someone from the org to attempt login on staging-coder.ddev.com before updating production. + +4. **Update this table**: Add the org to the sponsor org table above so the list stays accurate. + +5. **Notify the sponsor**: Send the sponsor notification (see `docs/admin/coder-ddev-com/sponsor-notification.md`) letting them know their org members now have access. + +**If the sponsor has no GitHub org**: Add their individual GitHub username to the `coder-ddev-com` org instead (see "Managing individual access" above). No server restart needed. +``` + +**Validation**: +- [ ] The five-step process is present +- [ ] The "no GitHub org" fallback path is documented +- [ ] References to staging-first are consistent with C-004 + +--- + +## Definition of Done + +- [ ] `docs/admin/server-setup.md` contains the full 27-org `ALLOWED_ORGS` value +- [ ] A staging OAuth App sub-section is present with correct callback URL +- [ ] `coder-ddev-com` org purpose and management is documented +- [ ] Sponsor org table is present with all 25 sponsor orgs +- [ ] "Adding a new sponsor org" runbook is present +- [ ] `terraform fmt` is not applicable (Markdown only — run `git diff docs/admin/server-setup.md` to review) +- [ ] No broken Markdown links + +## Risks + +- The `ALLOWED_ORGS` value is long; a typo in a slug silently excludes that org's members. Verify the slug list against `kitty-specs/github-org-gated-signup-01KR1P4G/research.md` before committing. +- `Institute-for-Advanced-Studies` is unverified — note this clearly in the doc. + +## Activity Log + +- 2026-05-07T18:56:27Z – claude – shell_pid=25745 – Assigned agent via action command +- 2026-05-07T18:58:51Z – claude – shell_pid=25745 – Ready for review: server-setup.md updated with full 27-org ALLOWED_ORGS, staging OAuth App section, coder-ddev-com management docs, sponsor table, and runbook +- 2026-05-07T19:03:03Z – claude – shell_pid=25745 – Review passed: all subtasks complete and verified diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP02-user-management-access-runbook.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP02-user-management-access-runbook.md new file mode 100644 index 0000000..78f7370 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP02-user-management-access-runbook.md @@ -0,0 +1,240 @@ +--- +work_package_id: WP02 +title: Update user-management.md with Access Runbook +dependencies: [] +requirement_refs: +- C-001 +- FR-001 +- FR-002 +- FR-003 +- FR-004 +- FR-009 +planning_base_branch: 20260507_speckitty +merge_target_branch: 20260507_speckitty +branch_strategy: Planning artifacts for this feature were generated on 20260507_speckitty. During /spec-kitty.implement this WP may branch from a dependency-specific base, but completed changes must merge back into 20260507_speckitty unless the human explicitly redirects the landing branch. +base_branch: kitty/mission-github-org-gated-signup-01KR1P4G +base_commit: e125c1d60f5ea0f1c84475d06a5947d15b201b60 +created_at: '2026-05-07T18:59:05.937727+00:00' +subtasks: +- T006 +- T007 +- T008 +- T009 +shell_pid: "29068" +agent: "claude" +history: +- date: '2026-05-07' + event: created +authoritative_surface: docs/admin/user-management.md +execution_mode: code_change +owned_files: +- docs/admin/user-management.md +tags: [] +--- + +# WP02 — Update user-management.md with Access Runbook + +## Branch Strategy + +- **Planning base**: `20260507_speckitty` +- **Merge target**: `20260507_speckitty` +- Implement directly on `20260507_speckitty`. Run `spec-kitty agent action implement WP02 --agent claude`. + +## Objective + +Add a clear "Access Management" section to `docs/admin/user-management.md` covering: +- How to grant individual access via the `coder-ddev-com` GitHub org +- How to pre-create password exception accounts for users who cannot use GitHub OAuth +- Notes on private org membership (private is sufficient — users need not publicize) +- The initial list of `coder-ddev-com` members to add when the org is created + +## Context + +With the org-gated signup model, access to coder.ddev.com is no longer open. Operators need a clear day-to-day runbook for the two ways to grant access: + +1. **Add to `coder-ddev-com` org** — the normal path for individuals not in `ddev` or a sponsor org +2. **Pre-create a password account** — the escape hatch for users who cannot authenticate via GitHub OAuth + +The existing `docs/admin/user-management.md` has good content on Coder user management but says nothing about the org-gated access model. This WP adds a new top-level section that operators can turn to immediately after deployment. + +**Read the current file before editing**: `docs/admin/user-management.md`. The file is 496 lines. Add the new section after the existing "User Accounts" section (currently at the top of the file) — specifically, insert the new "## Access Management" section after line ~60 (after the User Roles subsection), before the existing section that follows it. + +--- + +## Subtask T006 — Add "Access Management" Section Header and Intro + +**Purpose**: Create the section that groups all access-granting procedures, so operators know where to look. + +**Location**: `docs/admin/user-management.md`, as a new top-level `## Access Management` section. Insert it after the "User Roles" subsection (the block ending around line 60) and before the next major section that follows. + +**Content to add**: + +```markdown +## Access Management + +coder.ddev.com restricts new GitHub OAuth signups to members of specific GitHub organizations. This section documents how to grant or revoke access for individuals who are not already in the `ddev` org or a $100+/month sponsor org. + +**The two access paths:** + +1. **`coder-ddev-com` org membership** — For individuals who should have ongoing access. Adding them to the `coder-ddev-com` GitHub org allows them to sign in immediately with no server change. + +2. **Password exception account** — For individuals who cannot or will not use GitHub OAuth. An admin pre-creates their Coder account with a password credential. + +See `docs/admin/server-setup.md` for the full `CODER_OAUTH2_GITHUB_ALLOWED_ORGS` configuration and sponsor org policy. +``` + +**Validation**: +- [ ] Section heading is `## Access Management` (top-level) +- [ ] Both access paths are mentioned +- [ ] Link to server-setup.md is present + +--- + +## Subtask T007 — Document Granting Access via `coder-ddev-com` Org Membership + +**Purpose**: Give operators the exact steps to add an individual to the `coder-ddev-com` GitHub org. + +**Location**: Add a sub-section `### Granting access via coder-ddev-com org membership` immediately inside the "Access Management" section. + +**Content to add**: + +```markdown +### Granting access via `coder-ddev-com` org membership + +Adding someone to the `coder-ddev-com` GitHub org grants them signup access to coder.ddev.com without requiring a Coder server restart. + +**Steps:** +1. Go to [github.com/coder-ddev-com](https://github.com/coder-ddev-com) → **People** → **Invite member** +2. Enter the person's GitHub username and send the invitation +3. Once they accept, they can sign in to coder.ddev.com via GitHub OAuth + +**Requirements:** +- You must be an owner of the `coder-ddev-com` GitHub org to send invitations +- The invitee must have a GitHub account +- They do **not** need to make their membership public — private membership is sufficient (see note on private membership below) + +**Removing access:** +1. Go to [github.com/coder-ddev-com](https://github.com/coder-ddev-com) → **People** +2. Click the member's username → **Remove from organization** +3. Their Coder account remains (they can still log in if they are a `ddev` org member or a member of a sponsor org); their `coder-ddev-com` access path is removed + +**Note**: Removing a user from `coder-ddev-com` does not delete their Coder workspace or account. If full account removal is needed, also delete the user account in Coder (see "Removing User Accounts" below). +``` + +**Validation**: +- [ ] Steps 1–3 for invitation are present +- [ ] Requirements (org owner, no public membership required) are listed +- [ ] Removal steps are present +- [ ] Note about account vs. org membership distinction is present + +--- + +## Subtask T008 — Document Pre-Creating Password Exception Accounts + +**Purpose**: Operators need to know how to create accounts for users who cannot use GitHub OAuth. This is the "escape hatch" path that keeps password auth viable. + +**Location**: Add a sub-section `### Pre-creating password exception accounts` inside the "Access Management" section, after the `coder-ddev-com` sub-section. + +**Content to add**: + +```markdown +### Pre-creating password exception accounts + +For users who cannot or will not authenticate via GitHub OAuth, an admin can pre-create their Coder account with a password credential. This path depends on password authentication being enabled — **`CODER_DISABLE_PASSWORD_AUTH` must never be set to `true`**. + +**When to use this path:** +- The user does not have a GitHub account +- The user has a GitHub account but cannot authorize the OAuth app (e.g., corporate GitHub account with SSO restrictions) +- Emergency admin access is needed without GitHub + +**Steps (via CLI):** +```bash +# Create a user account with email and password prompt +coder users create --email --set-password + +# Or set a specific password (use a strong, random value — user should change it on first login) +coder users create --email --password "" +``` + +**After creating the account:** +1. Share the username and temporary password with the user via a secure channel (not email in plaintext) +2. Instruct the user to change their password on first login: **User Settings → Security → Update Password** +3. Confirm the user can log in at coder.ddev.com before closing the support request + +**Roles:** Accounts created this way default to the "Member" role. If a higher role is needed: +```bash +coder users edit-roles --roles template-admin +``` + +**Note**: Password accounts are exempt from the GitHub org membership check. They can log in regardless of GitHub org membership. +``` + +**Validation**: +- [ ] The `CODER_DISABLE_PASSWORD_AUTH` constraint is mentioned explicitly +- [ ] CLI commands for creating a user are correct +- [ ] The "when to use" list is present +- [ ] Password change instruction is included + +--- + +## Subtask T009 — Add Private Membership Note and Initial `coder-ddev-com` Members + +**Purpose**: Operators need to know that private GitHub org membership works (no extra action needed from users), and they need the initial member list to populate the org when it is created. + +**Location**: Add a sub-section `### Private org membership` inside the "Access Management" section, after the password exception sub-section. Then add a sub-section `### Initial coder-ddev-com members` after it. + +**Content to add**: + +```markdown +### Private org membership + +Users do **not** need to make their `coder-ddev-com` (or `ddev`) org membership public. The Coder server uses the `read:org` OAuth scope, which allows it to check org membership regardless of visibility. Private membership is sufficient. + +If a user reports that they accepted the `coder-ddev-com` invitation but still cannot log in, check: +1. They accepted the invitation (invitation emails expire after 7 days) +2. They are signing in at the correct URL (coder.ddev.com, not an old bookmark to an open-signup URL) +3. Their Coder account exists — if they have never signed in before, they will be creating a new account on first GitHub OAuth login + +### Initial `coder-ddev-com` members + +When the `coder-ddev-com` GitHub org is created, add these initial members: + +| GitHub username | Reason | +| --------------- | ------ | +| `dougvann` | Individual $100/month GitHub Sponsor (confirmed via [ddev/ddev.com#626](https://github.com/ddev/ddev.com/pull/626)) | +| `claudiu-cristea` | Webikon sponsor — ddev.com sponsor link points to individual, not org | + +**Pending — add when GitHub usernames are confirmed:** +- LetsTalk — no GitHub org found; add individual username when known +- Amedick Sommer — no GitHub org found; add individual username when known +- Pottkinder GmbH — no GitHub org found; add individual username when known +``` + +**Validation**: +- [ ] Private membership note is present with explanation of `read:org` scope +- [ ] The troubleshooting checklist for "accepted invite but can't log in" is present +- [ ] `dougvann` and `claudiu-cristea` are in the initial members table +- [ ] Pending members (LetsTalk, Amedick Sommer, Pottkinder) are listed + +--- + +## Definition of Done + +- [ ] `docs/admin/user-management.md` has a `## Access Management` top-level section +- [ ] `coder-ddev-com` org invitation process is documented step-by-step +- [ ] Password exception account creation is documented with CLI commands +- [ ] `CODER_DISABLE_PASSWORD_AUTH` constraint is mentioned +- [ ] Private org membership behavior is explained +- [ ] Initial `coder-ddev-com` member list is present with `dougvann` and `claudiu-cristea` +- [ ] No broken Markdown links + +## Risks + +- The `coder users create` CLI syntax should be verified against the current Coder CLI (`coder users create --help`) before committing — flags may differ across Coder versions. +- Avoid adding the "Access Management" section in a location that breaks the existing document flow. Insert it after the "User Roles" content, not at the very end. + +## Activity Log + +- 2026-05-07T18:59:06Z – claude – shell_pid=29068 – Assigned agent via action command +- 2026-05-07T18:59:50Z – claude – shell_pid=29068 – Ready for review: Access Management section added to user-management.md +- 2026-05-07T19:03:24Z – claude – shell_pid=29068 – Review passed: Access Management section complete with all 4 subtask topics diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP03-coder-ddev-com-org-content.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP03-coder-ddev-com-org-content.md new file mode 100644 index 0000000..33401d3 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP03-coder-ddev-com-org-content.md @@ -0,0 +1,310 @@ +--- +work_package_id: WP03 +title: coder-ddev-com Org Content Drafts +dependencies: [] +requirement_refs: +- FR-002 +- FR-010 +- FR-011 +- FR-015 +planning_base_branch: 20260507_speckitty +merge_target_branch: 20260507_speckitty +branch_strategy: Planning artifacts for this feature were generated on 20260507_speckitty. During /spec-kitty.implement this WP may branch from a dependency-specific base, but completed changes must merge back into 20260507_speckitty unless the human explicitly redirects the landing branch. +subtasks: +- T010 +- T011 +- T012 +- T013 +agent: "claude" +shell_pid: "30615" +history: +- date: '2026-05-07' + event: created +authoritative_surface: docs/admin/coder-ddev-com/ +execution_mode: planning_artifact +owned_files: +- docs/admin/coder-ddev-com/** +tags: [] +--- + +# WP03 — coder-ddev-com Org Content Drafts + +## Branch Strategy + +- **Planning base**: `20260507_speckitty` +- **Merge target**: `20260507_speckitty` +- Implement directly on `20260507_speckitty`. Run `spec-kitty agent action implement WP03 --agent claude`. + +## Objective + +Produce all content needed to stand up the `coder-ddev-com` GitHub organization: + +1. **Org profile README** — displayed on the org's GitHub page; explains the org's purpose +2. **access-requests repo README** — explains how to open an access request +3. **Access request GitHub issue template** — the structured form prospective users fill out +4. **Sponsor notification message** — the message sent to newly-added sponsor orgs + +All content is committed to `docs/admin/coder-ddev-com/` in this repo for operator use. The operator copies them to the appropriate GitHub locations when setting up the org. + +## Context + +The `coder-ddev-com` GitHub organization does not yet exist. When an operator creates it, they need ready-to-use content for: + +- `.github/profile/README.md` in the org — the GitHub org profile (shown on the org's main page) +- `coder-ddev-com/access-requests` repo — a public repo where prospective users open issues requesting access +- The issue template in that repo (`.github/ISSUE_TEMPLATE/access-request.yml`) +- A short notification message to send to sponsor org owners/maintainers when their org is added to `ALLOWED_ORGS` + +**Create new files** — none of these files exist yet. Create the `docs/admin/coder-ddev-com/` directory and write each file fresh. + +--- + +## Subtask T010 — Write `coder-ddev-com` Org Profile README Draft + +**Purpose**: The org profile README is the first thing visitors see when they visit `github.com/coder-ddev-com`. It must explain the org's purpose, who qualifies, and how membership grants coder.ddev.com access. + +**File to create**: `docs/admin/coder-ddev-com/org-profile-README.md` + +**Operator instruction**: Copy this file's content to `.github/profile/README.md` in the `coder-ddev-com` GitHub org (create a `.github` repo in the org if it does not exist, then add `profile/README.md`). + +**Content to write**: + +```markdown +# coder-ddev-com + +This organization is the managed access list for [coder.ddev.com](https://coder.ddev.com) — a cloud-based DDEV development environment for web developers. + +## What is coder.ddev.com? + +[coder.ddev.com](https://coder.ddev.com) provides cloud-hosted workspaces running [DDEV](https://ddev.com), an open-source local development environment tool for PHP, Node.js, Python, and more projects. Workspaces include VS Code for Web, a terminal, Docker-in-Docker, and full DDEV support — no local setup required. + +## How membership works + +Members of this organization can sign in to coder.ddev.com using GitHub OAuth. Membership is granted by a coder-ddev-com org owner. + +You do **not** need to make your membership public. Private membership is sufficient. + +## Who qualifies? + +Access is available to: + +- Members of the [ddev](https://github.com/ddev) GitHub org +- Members of organizations that sponsor DDEV at $100+/month +- Individuals approved by the DDEV maintainers (this org) + +## Requesting access + +If you do not have access through one of the paths above, open an issue in the [access-requests](https://github.com/coder-ddev-com/access-requests) repository. + +## Questions + +Visit the [DDEV Discord](https://discord.ddev.com) or open an issue in [ddev/ddev](https://github.com/ddev/ddev/issues). +``` + +**Validation**: +- [ ] File is at `docs/admin/coder-ddev-com/org-profile-README.md` +- [ ] Operator instruction (where to copy it) is present as a comment or note at the top of the file +- [ ] Link to access-requests repo is present +- [ ] Private membership note is present +- [ ] The three access tiers (ddev org, sponsor orgs, approved individuals) are mentioned + +--- + +## Subtask T011 — Write access-requests Repo README Draft + +**Purpose**: The `coder-ddev-com/access-requests` repo is where prospective users open issues requesting access. Its README explains what the repo is for and how the process works. + +**File to create**: `docs/admin/coder-ddev-com/access-requests-README.md` + +**Operator instruction**: Create a public repo named `access-requests` in the `coder-ddev-com` GitHub org. Copy this file's content to `README.md` in that repo. + +**Content to write**: + +```markdown +# coder-ddev-com / access-requests + +This repository is the access request tracker for [coder.ddev.com](https://coder.ddev.com). + +## What is coder.ddev.com? + +[coder.ddev.com](https://coder.ddev.com) is a cloud-hosted DDEV development environment for web developers. It provides workspaces running [DDEV](https://ddev.com) with VS Code for Web, a terminal, and full Docker support. + +## Who already has access? + +Access is automatically available (no request needed) if you are: + +- A member of the [ddev](https://github.com/ddev) GitHub org +- A member of an organization that sponsors DDEV at $100+/month + +See the [DDEV sponsors page](https://ddev.com/support-ddev/) for the current list of sponsors. + +## Requesting access + +If you do not have access through one of the above paths, open an issue in this repository using the **Access Request** template. + +We will review your request and, if approved, add you to the `coder-ddev-com` org. You will receive a GitHub invitation — once you accept it, you can sign in to coder.ddev.com with your GitHub account. + +**What to include in your request:** +- Your GitHub username +- Why you want access (brief description — DDEV user, contributor, etc.) + +## Response time + +We aim to respond to requests within a few business days. If you have not heard back in a week, feel free to ping the issue. + +## Questions + +Visit the [DDEV Discord](https://discord.ddev.com) or open an issue in [ddev/ddev](https://github.com/ddev/ddev/issues). +``` + +**Validation**: +- [ ] File is at `docs/admin/coder-ddev-com/access-requests-README.md` +- [ ] Operator instruction (where to copy it) is present +- [ ] "Who already has access" section explains automatic access tiers +- [ ] Clear instructions for opening a request are present +- [ ] Response time expectation is set + +--- + +## Subtask T012 — Write Access Request GitHub Issue Template Draft + +**Purpose**: The issue template structures the access request so reviewers get the information they need (GitHub username, reason for access) without back-and-forth. + +**File to create**: `docs/admin/coder-ddev-com/access-request-issue-template.yml` + +**Operator instruction**: In the `coder-ddev-com/access-requests` repo, create `.github/ISSUE_TEMPLATE/access-request.yml` with this content. + +**Content to write**: + +```yaml +name: Access Request +description: Request access to coder.ddev.com +title: "Access request: [your GitHub username]" +labels: ["access-request"] +body: + - type: markdown + attributes: + value: | + Thanks for your interest in coder.ddev.com! Please fill out the form below. + + **Already have access?** If you are a member of the [ddev](https://github.com/ddev) org + or a $100+/month DDEV sponsor org, you should already have access — try signing in at + [coder.ddev.com](https://coder.ddev.com) first. + + - type: input + id: github_username + attributes: + label: GitHub username + description: Your GitHub username (the one you will use to sign in) + placeholder: "e.g. octocat" + validations: + required: true + + - type: textarea + id: reason + attributes: + label: Why do you want access? + description: Brief description — DDEV contributor, open-source developer, evaluating DDEV for a project, etc. + placeholder: "I maintain a DDEV add-on and want to test it in a cloud workspace..." + validations: + required: true + + - type: checkboxes + id: confirm + attributes: + label: Confirmation + options: + - label: I have checked that I do not already have access through the ddev org or a sponsor org + required: true + - label: I understand this is a shared resource and will use it responsibly + required: true +``` + +**Validation**: +- [ ] File is at `docs/admin/coder-ddev-com/access-request-issue-template.yml` +- [ ] Operator instruction is present +- [ ] `github_username` field is required +- [ ] `reason` textarea is required +- [ ] Confirmation checkboxes are present +- [ ] The pre-check note (ddev org / sponsor org members already have access) is included + +--- + +## Subtask T013 — Write Sponsor Notification Message Template + +**Purpose**: When a new sponsor org is added to `ALLOWED_ORGS`, the DDEV maintainers should notify that org's owners/maintainers so they know their members have access. This template is the message to send. + +**File to create**: `docs/admin/coder-ddev-com/sponsor-notification.md` + +**Operator instruction**: Use this as the text of an email, GitHub issue, or direct message to the sponsor org's maintainer(s). Substitute `[ORG NAME]` and `[GITHUB ORG SLUG]` before sending. + +**Content to write**: + +```markdown + + +Subject: coder.ddev.com access for [ORG NAME] org members + +Hi, + +As a $100+/month sponsor of DDEV, all members of the **[ORG NAME]** GitHub organization ([github.com/[GITHUB ORG SLUG]](https://github.com/[GITHUB ORG SLUG])) now have access to **[coder.ddev.com](https://coder.ddev.com)** — a cloud-hosted DDEV development environment. + +**What is coder.ddev.com?** + +coder.ddev.com provides on-demand cloud workspaces running DDEV with: +- VS Code for Web (full IDE in the browser) +- A terminal +- Docker-in-Docker via Sysbox (full DDEV support, all project types) + +No local setup required — spin up a workspace, start DDEV, and get coding. + +**How to access:** + +1. Visit [coder.ddev.com](https://coder.ddev.com) +2. Click **Sign in with GitHub** +3. Authorize the DDEV Coder app +4. You're in — create a workspace from the Drupal Core, Drupal Contrib, or Freeform template + +All members of the [ORG NAME] GitHub org can sign in. They do not need to be added individually. Org membership does not need to be public. + +**Questions or issues?** + +- [DDEV Discord](https://discord.ddev.com) — `#coder-ddev-com` channel (or any channel) +- [ddev/ddev issues](https://github.com/ddev/ddev/issues) + +Thank you for supporting DDEV! + +— The DDEV maintainers +``` + +**Validation**: +- [ ] File is at `docs/admin/coder-ddev-com/sponsor-notification.md` +- [ ] `[ORG NAME]` and `[GITHUB ORG SLUG]` placeholders are present and clearly marked +- [ ] Operator substitution note is at the top +- [ ] What coder.ddev.com is and how to access it are both explained +- [ ] Contact options (Discord, issues) are present + +--- + +## Definition of Done + +- [ ] `docs/admin/coder-ddev-com/` directory exists with four files: + - `org-profile-README.md` + - `access-requests-README.md` + - `access-request-issue-template.yml` + - `sponsor-notification.md` +- [ ] Each file has an operator instruction note explaining where to copy it +- [ ] Org profile README mentions all three access tiers and links to access-requests repo +- [ ] Issue template is valid YAML with required fields +- [ ] Sponsor notification has clearly marked placeholders + +## Risks + +- The `coder-ddev-com/access-requests` repo URL is referenced in T011 and the org profile README — the repo does not exist yet. Use the anticipated URL `https://github.com/coder-ddev-com/access-requests` in drafts; operator creates the repo before publishing. +- The issue template YAML syntax must be valid. The `type` values (`input`, `textarea`, `checkboxes`, `markdown`) are the standard GitHub issue form types. + +## Activity Log + +- 2026-05-07T19:00:15Z – claude – shell_pid=30615 – Started implementation via action command +- 2026-05-07T19:01:31Z – claude – shell_pid=30615 – Ready for review: org-profile-README, access-requests-README, issue template, sponsor notification all created in docs/admin/coder-ddev-com/ +- 2026-05-07T19:04:01Z – claude – shell_pid=30615 – Review passed: all 4 files created with operator notes. YAML template structure verified. diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP04-blog-post-draft.md b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP04-blog-post-draft.md new file mode 100644 index 0000000..9bef68b --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/tasks/WP04-blog-post-draft.md @@ -0,0 +1,189 @@ +--- +work_package_id: WP04 +title: Blog Post Update Draft +dependencies: +- WP03 +requirement_refs: +- FR-012 +planning_base_branch: 20260507_speckitty +merge_target_branch: 20260507_speckitty +branch_strategy: Planning artifacts for this feature were generated on 20260507_speckitty. During /spec-kitty.implement this WP may branch from a dependency-specific base, but completed changes must merge back into 20260507_speckitty unless the human explicitly redirects the landing branch. +subtasks: +- T014 +- T015 +- T016 +agent: "claude" +shell_pid: "32508" +history: +- date: '2026-05-07' + event: created +authoritative_surface: docs/admin/blog-post-draft.md +execution_mode: planning_artifact +owned_files: +- docs/admin/blog-post-draft.md +tags: [] +--- + +# WP04 — Blog Post Update Draft + +## Branch Strategy + +- **Planning base**: `20260507_speckitty` +- **Merge target**: `20260507_speckitty` +- **Depends on WP03** — needs the `coder-ddev-com/access-requests` repo URL established in WP03. +- Implement directly on `20260507_speckitty`. Run `spec-kitty agent action implement WP04 --agent claude`. + +## Objective + +Produce a ready-to-apply patch for `ddev/ddev.com/src/content/blog/coder-ddev-com-announcement.md` that updates the auth description to reflect the org-gated signup model. The patch is committed to `docs/admin/blog-post-draft.md` in this repo; an operator applies it to the `ddev/ddev.com` repo as a separate PR. + +The existing blog post implies open signup ("No separate account needed"). That must be replaced with accurate language explaining: +- Signups are now restricted to org members +- How access works (ddev org, sponsor orgs, coder-ddev-com org) +- Where to request access (access-requests repo) + +## Context + +The blog post lives in a separate repo (`ddev/ddev.com`) and requires its own PR. This WP does not modify that repo directly — it produces a draft that the operator applies. + +**WP03 dependency**: The `coder-ddev-com/access-requests` URL (`https://github.com/coder-ddev-com/access-requests`) is used in the draft. WP03 establishes the content for that repo. Confirm WP03 is complete before finalizing this draft. + +**Target file in `ddev/ddev.com`**: `src/content/blog/coder-ddev-com-announcement.md` + +The draft format in `docs/admin/blog-post-draft.md` should be written so an operator can: +1. Open the file +2. Identify exactly what sections to change in the original blog post +3. Apply the changes with minimal effort (ideally a diff or annotated replacement blocks) + +--- + +## Subtask T014 — Update "Log In with GitHub" Section + +**Purpose**: The current blog post has a section explaining GitHub login ("No separate account needed"). This must be updated to explain that signups are now restricted. + +**File to create**: `docs/admin/blog-post-draft.md` + +**Operator instruction**: Apply the changes in this file to `ddev/ddev.com/src/content/blog/coder-ddev-com-announcement.md` and open a PR to `ddev/ddev.com`. + +First, create the file with a header explaining what this file is and how to use it, followed by the draft content. + +**Content structure for the file**: + +```markdown +# Blog Post Update Draft: coder.ddev.com Org-Gated Signup + +**Target file**: `ddev/ddev.com/src/content/blog/coder-ddev-com-announcement.md` +**Action**: Apply these section replacements to the blog post, then open a PR to `ddev/ddev.com` + +--- + +## Section: "Log In with GitHub" — Replace existing content + +Find the section in the blog post that explains GitHub login. Replace the existing content of that section with the following: + +--- + +### Log In with GitHub + +Access to coder.ddev.com requires a GitHub account. Sign in using the **Sign in with GitHub** button — no separate Coder account registration is needed. + +**Who has access:** + +- Members of the [ddev](https://github.com/ddev) GitHub organization +- Members of organizations that sponsor DDEV at $100+/month (see the [DDEV sponsors page](https://ddev.com/support-ddev/)) +- Individuals approved by the DDEV maintainers + +If you are a `ddev` org member or your organization is a $100+/month sponsor, you can sign in immediately — no request needed. + +--- +``` + +**Validation**: +- [ ] File is created at `docs/admin/blog-post-draft.md` +- [ ] Operator instruction (target file + how to apply) is at the top +- [ ] "Who has access" lists all three tiers +- [ ] The old "No separate account needed" language is replaced (not just appended) + +--- + +## Subtask T015 — Add Access Restriction Paragraph and Access Paths + +**Purpose**: Add a paragraph explaining that signups are restricted and describing the two paths to access: already-in-an-org, or request access. + +**Location**: In `docs/admin/blog-post-draft.md`, append to the section started in T014. + +**Content to append to the draft**: + +```markdown +## Section: Access Restriction and Request Path — Add after "Log In with GitHub" + +Add the following as a new paragraph or subsection immediately after the "Log In with GitHub" section: + +--- + +### Requesting Access + +If you do not have access through one of the paths above, you can request it by opening an issue in the [coder-ddev-com/access-requests](https://github.com/coder-ddev-com/access-requests) repository on GitHub. Include your GitHub username and a brief description of how you plan to use the environment. + +The DDEV maintainers review requests and add approved users to the `coder-ddev-com` GitHub organization. Once added, you can sign in immediately — no server restart needed on our end. + +--- +``` + +**Validation**: +- [ ] Link to `https://github.com/coder-ddev-com/access-requests` is present and correct +- [ ] The process (open issue → review → org invite → sign in) is explained +- [ ] The section is clearly marked as "add after" the previous section + +--- + +## Subtask T016 — Add Sponsor Org Access Benefit Mention + +**Purpose**: Sponsor organizations may not know their members can now log in. Adding a mention in the blog post (and the sponsor notification in WP03) covers the announcement angle. + +**Location**: In `docs/admin/blog-post-draft.md`, append a final section. + +**Content to append to the draft**: + +```markdown +## Section: Sponsor Org Access — Add to "Sponsors" or "Support DDEV" section + +If the blog post has a section mentioning DDEV sponsors, add the following sentence or short paragraph. If no such section exists, add it as a standalone callout near the end of the post: + +--- + +**Sponsor org access**: Organizations that sponsor DDEV at $100+/month receive access as an org-level benefit — all members of a sponsor's GitHub organization can sign in to coder.ddev.com without individual enrollment. See the [DDEV sponsors page](https://ddev.com/support-ddev/) if your organization is interested in sponsoring. + +--- + +**Note for operator**: If the blog post already has a sponsors callout, integrate this sentence rather than adding a duplicate section. +``` + +**Validation**: +- [ ] The sponsor benefit (org-level, all members) is stated clearly +- [ ] Link to ddev.com/support-ddev/ is present +- [ ] Operator note about avoiding duplicates is present + +--- + +## Definition of Done + +- [ ] `docs/admin/blog-post-draft.md` exists and has three clearly delineated sections: + 1. Updated "Log In with GitHub" content + 2. New "Requesting Access" paragraph with link to access-requests repo + 3. Sponsor org access benefit mention +- [ ] Operator instructions at the top of the file explain the target file and how to apply changes +- [ ] All three access tiers are mentioned somewhere in the draft +- [ ] The `coder-ddev-com/access-requests` URL is correct +- [ ] No broken Markdown links + +## Risks + +- The actual blog post content is in a separate repo (`ddev/ddev.com`) that this agent does not have direct access to. The draft is produced without reading the original post, so section names in "Find and replace" instructions are approximate. The operator will need to locate the correct sections by reading the original. +- If the blog post structure has changed significantly since the announcement, the operator may need to adapt the draft rather than apply it verbatim. + +## Activity Log + +- 2026-05-07T19:01:42Z – claude – shell_pid=32508 – Started implementation via action command +- 2026-05-07T19:02:14Z – claude – shell_pid=32508 – Ready for review: blog-post-draft.md created with 3 sections +- 2026-05-07T19:04:15Z – claude – shell_pid=32508 – Review passed: blog-post-draft.md has all 3 required sections and correct access-requests link diff --git a/kitty-specs/github-org-gated-signup-01KR1P4G/wps.yaml b/kitty-specs/github-org-gated-signup-01KR1P4G/wps.yaml new file mode 100644 index 0000000..2e2b8b7 --- /dev/null +++ b/kitty-specs/github-org-gated-signup-01KR1P4G/wps.yaml @@ -0,0 +1,74 @@ +work_packages: + - id: WP01 + title: "Update server-setup.md for Org-Gated Auth" + dependencies: [] + owned_files: + - "docs/admin/server-setup.md" + requirement_refs: + - FR-001 + - FR-005 + - FR-006 + - FR-007 + - FR-013 + - FR-014 + - C-005 + - C-006 + - C-007 + subtasks: + - T001 + - T002 + - T003 + - T004 + - T005 + prompt_file: "tasks/WP01-server-setup-update.md" + + - id: WP02 + title: "Update user-management.md with Access Runbook" + dependencies: [] + owned_files: + - "docs/admin/user-management.md" + requirement_refs: + - FR-002 + - FR-003 + - FR-004 + - FR-009 + - NFR-002 + - C-001 + subtasks: + - T006 + - T007 + - T008 + - T009 + prompt_file: "tasks/WP02-user-management-access-runbook.md" + + - id: WP03 + title: "coder-ddev-com Org Content Drafts" + dependencies: [] + owned_files: + - "docs/admin/coder-ddev-com/**" + requirement_refs: + - FR-010 + - FR-011 + - FR-015 + - C-002 + subtasks: + - T010 + - T011 + - T012 + - T013 + prompt_file: "tasks/WP03-coder-ddev-com-org-content.md" + + - id: WP04 + title: "Blog Post Update Draft" + dependencies: + - WP03 + owned_files: + - "docs/admin/blog-post-draft.md" + requirement_refs: + - FR-012 + - FR-008 + subtasks: + - T014 + - T015 + - T016 + prompt_file: "tasks/WP04-blog-post-draft.md"