- Document service account read-only limitation - Add session 4 log (1Password token update, task tracking) - Track expired token fix in cto-executive-system#8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
Gitea Infrastructure Handover Document
Overview
Self-hosted Gitea instance running on bigbox server with S3 storage, PostgreSQL, and Prometheus monitoring.
URL: https://git.terraphim.cloud GitHub Mirror: https://github.com/terraphim/gitea-infrastructure
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Internet │
└─────────────────────────┬───────────────────────────────────┘
│ HTTPS (port 443)
▼
┌─────────────────────────────────────────────────────────────┐
│ Caddy Reverse Proxy │
│ (terraphim.cloud, existing) │
└─────────────────────────┬───────────────────────────────────┘
│ git.terraphim.cloud -> localhost:3000
▼
┌─────────────────────────────────────────────────────────────┐
│ Docker Network (gitea) │
├─────────────────────────────────────────────────────────────┤
│ Gitea (port 3000) │ PostgreSQL │ SeaweedFS (S3) │
│ - Attachments │ (port 5432) │ - S3 (port 8333) │
│ - Avatars │ │ - Filer (port 8888) │
│ - LFS │ │ - Volume (port 8080) │
│ │ │ - Master (port 9333) │
└─────────────────────────────────────────────────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Tailscale Network │
│ Prometheus (100.106.66.7:9000) - internal │
│ S3 (100.106.66.7:8333) - internal only │
└─────────────────────────────────────────────────────────────┘
Services
| Service | Port | Access | Description |
|---|---|---|---|
| Gitea | 3000 | localhost (via Caddy) | Git web UI |
| Gitea SSH | 22/222 | Public | Git over SSH |
| PostgreSQL | 5432 | Docker internal | Database |
| SeaweedFS S3 | 8333 | Tailscale only | S3-compatible storage |
| SeaweedFS Filer | 8888 | Docker internal | File interface |
| Prometheus | 9000 | Tailscale only | Metrics |
Server Access
Server: bigbox (100.106.66.7)
# SSH to bigbox
ssh bigbox
# Navigate to stack directory
cd ~/gitea-stack
# View container status
docker compose ps
# View logs
docker compose logs -f gitea
# Restart Gitea
docker compose restart server
# Stop entire stack
docker compose down
# Start stack
docker compose up -d
1Password Secrets
Vault: TerraphimPlatform
| Item | Purpose |
|---|---|
| gitea-postgres | PostgreSQL password |
| gitea-s3 | S3 access key and secret key |
Re-deploying with 1Password
-
Source the 1Password authentication:
source ~/op_zesticai_non_prod.sh -
Inject secrets and deploy:
cd ~/gitea-stack op inject --in-file ../gitea_config/docker-compose.yml.template --out-file docker-compose.yml docker compose up -d
Storage Configuration
S3 Buckets
- gitea - Attachments, avatars, repository files
- gitea-lfs - Git LFS objects
Gitea Storage Settings
The following storage types are configured to use S3 (Minio):
[attachment]- File attachments[picture]- Avatars and images[lfs]- Git LFS objects[storage]- Default storage configuration
Local storage is used for:
- Git repositories (
/data/git/repositories) - Database (PostgreSQL)
Caddy Configuration
Caddy is already running on bigbox. The git route is appended to /etc/caddy/Caddyfile:
git.terraphim.cloud {
reverse_proxy localhost:3000
}
To update Caddy config:
ssh bigbox
sudo vim /etc/caddy/Caddyfile
sudo caddy reload
Monitoring
Prometheus: http://100.106.66.7:9000 (Tailscale only)
Scrape targets configured in prometheus/prometheus.yml.
Troubleshooting
Check Gitea status
ssh bigbox
docker compose ps
docker logs gitea
Check S3 connectivity
ssh bigbox
docker exec gitea curl -s http://s3storage:8333/
Reset PostgreSQL password
ssh bigbox
docker compose exec db psql -U gitea -c "ALTER USER gitea WITH PASSWORD 'newpassword';"
View S3 buckets
ssh bigbox
curl -s http://localhost:8888/bucket/
Backup
To back up the Gitea data:
# On bigbox
cd ~/gitea-stack
docker compose stop
tar -czf gitea-backup-$(date +%Y%m%d).tar.gz gitea/ postgres/ seaweedfs/
docker compose start
Updates
To update Gitea to a new version:
-
Edit
docker-compose.yml.template:image: gitea/gitea:NEW_VERSION -
Re-inject secrets and deploy:
source ~/op_zesticai_non_prod.sh op inject --in-file ../gitea_config/docker-compose.yml.template --out-file docker-compose.yml docker compose up -d --build
GitHub -> Gitea Mirroring
Gitea is configured as a pull mirror from GitHub. No GitHub Actions or manual push needed.
How it works: Gitea polls GitHub every hour and pulls new commits automatically.
| Setting | Value |
|---|---|
| Source | https://github.com/terraphim/gitea-infrastructure (private) |
| Mirror | https://git.terraphim.cloud/terraphim/gitea-infrastructure |
| Interval | 1 hour |
| Auth | GitHub token stored in Gitea migration config |
Manual Sync
source ~/op_zesticai_non_prod.sh
export GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential")
curl -s -X POST "https://git.terraphim.cloud/api/v1/repos/terraphim/gitea-infrastructure/mirror-sync" \
-H "Authorization: token $GITEA_TOKEN"
Recreating the Mirror
If the mirror breaks (e.g., GitHub token expires), delete and recreate:
source ~/op_zesticai_non_prod.sh
GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential")
GH_TOKEN=$(gh auth token)
# Delete existing
curl -s -X DELETE "https://git.terraphim.cloud/api/v1/repos/terraphim/gitea-infrastructure" \
-H "Authorization: token $GITEA_TOKEN"
sleep 10
# Recreate as pull mirror
curl -s -X POST "https://git.terraphim.cloud/api/v1/repos/migrate" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"clone_addr\": \"https://github.com/terraphim/gitea-infrastructure\",
\"repo_name\": \"gitea-infrastructure\",
\"repo_owner\": \"terraphim\",
\"service\": \"github\",
\"auth_token\": \"$GH_TOKEN\",
\"mirror\": true,
\"mirror_interval\": \"1h\"
}"
Key Constraints
- The Gitea repo is read-only (mirror). All pushes go to GitHub only.
- An existing repo cannot be converted to a mirror -- must delete and recreate via
POST /repos/migrate. service: "github"requires a valid GitHub token for private repos.service: "git"fails with "terminal prompts disabled".- The 1Password item
github.personal.tokenis expired. Usegh auth tokeninstead.
AI Agent Skill (Gitea Task Management)
Added 2026-02-18. Provides AI agents with project and task management via Gitea API.
Architecture
Dual-mode: gitea-mcp (75+ tools for standard CRUD) + curl/jq shell helpers (milestones, workflow labels, dependencies -- areas where gitea-mcp has gaps).
Key Constraint
Gitea 1.22.6 has NO project board REST API. Milestones serve as project containers with built-in progress tracking.
Files
| File | Description |
|---|---|
.agents/skills/gitea/SKILL.md |
Agent skill document with all helpers |
.agents/skills/gitea/setup-labels.sh |
Idempotent repo + 12 label creation |
.agents/skills/gitea/test-gitea-skill.sh |
10 integration tests (26 assertions) |
.docs/RESEARCH-gitea-agent-skill.md |
Phase 1 research (verified against live) |
.docs/DESIGN-gitea-agent-skill.md |
Phase 2 design document |
Default Task Repository
terraphim/agent-tasks -- overridable via GITEA_OWNER and GITEA_REPO env vars.
Authentication
source ~/op_zesticai_non_prod.sh
export GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential")
Workflow Labels
12 scoped labels across 3 scopes (workflow, priority, type). Workflow state machine: ready -> in-progress -> blocked -> done.
Running Tests
source ~/op_zesticai_non_prod.sh
export GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential")
.agents/skills/gitea/test-gitea-skill.sh
All 26 assertions pass against live Gitea 1.22.6 as of 2026-02-18.
Critical API Gotchas
- Label exclusivity is UI-only -- API POST
/labelsjust appends. Must use PUT with label swap pattern (get current, filterworkflow:*, add new, PUT all). - Assignees -- Use
PATCH /issues/{index}with{"assignees": [...]}. ThePOST /issues/{index}/assigneesendpoint does not exist in 1.22.6. - Issue dependencies -- Body uses
IssueMetaformat:{"owner": "...", "repo": "...", "index": N}(NOT{"depends_on_id": N}).
Pre-commit Hooks
Added .pre-commit-config.yaml with:
- Trailing whitespace, end-of-file fixer, YAML/JSON check
- Merge conflict and private key detection
- Secret detection via
detect-secrets(baseline at.secrets.baseline)
Key Files
| File | Description |
|---|---|
docker-compose.yml.template |
Template with 1Password references |
Caddyfile |
Caddy reverse proxy config |
deploy-to-bigbox.sh |
Deployment script |
s3_config.json.template |
SeaweedFS S3 config template |
prometheus/prometheus.yml |
Prometheus scrape config |
MIRRORING-GUIDE.md |
Gitea pull mirror setup (replaced GitHub Actions) |
.agents/skills/gitea/SKILL.md |
AI agent Gitea skill |
.agents/skills/gitea/setup-labels.sh |
Label setup script |
.agents/skills/gitea/test-gitea-skill.sh |
Integration tests |
.pre-commit-config.yaml |
Pre-commit hook config |
ARTICLE-agent-task-management.md |
CTO voice article (~2800 words) |
ARTICLE-agent-task-consolidation.md |
Technical reference article (~6000 words) |
lessons-learned.md |
Session discoveries and pitfalls |
Articles
Two comparison articles documenting the migration from beads/br/agent-mail to Gitea:
| File | Style | Words |
|---|---|---|
ARTICLE-agent-task-management.md |
CTO voice (bold, narrative) | ~2800 |
ARTICLE-agent-task-consolidation.md |
Technical reference (comprehensive) | ~6000 |
Both cover: 1Password integration, Gitea + SeaweedFS infrastructure, all three API gotchas with fix patterns, and honest tradeoff analysis. All commands verified working against live instance.
Gitea Mirror
Gitea pulls from GitHub automatically (1-hour interval). No GitHub Actions needed.
| Role | URL |
|---|---|
| Primary (push here) | https://github.com/terraphim/gitea-infrastructure |
| Mirror (auto-synced) | https://git.terraphim.cloud/terraphim/gitea-infrastructure |
GitHub Actions workflows (sync-to-gitea.yml, mirror-check.yml) were removed -- Gitea's native pull mirror replaces them.
Session Log (2026-02-18)
Session 1: Gitea Agent Skill Implementation
- Rewrote Gitea agent skill with verified API patterns (milestones, workflow labels, dependencies)
- Created setup-labels.sh for idempotent repo + label creation
- Created test-gitea-skill.sh with 10 tests (26 assertions), all passing
- Found and fixed 3 API behavior bugs (label exclusivity, assignee endpoint, dependency format)
- Added pre-commit hooks and fixed trailing whitespace across 15 files
- Conducted disciplined research and design phases with live API verification
Session 2: Articles and Publishing
- Wrote two comparison articles (CTO voice + technical reference) comparing beads/br/agent-mail with Gitea replacement
- All code snippets, commands, and configurations verified against live Gitea instance
- Pushed all commits to GitHub origin/main
Session 3: Replace GitHub Actions with Gitea Pull Mirror
- Removed
sync-to-gitea.ymlandmirror-check.ymlGitHub Actions workflows - Deleted manually-pushed Gitea repo, recreated as native pull mirror via
POST /repos/migrate - Discovered
github.personal.tokenin 1Password is expired (401); usedgh auth tokeninstead - Discovered Gitea creates empty mirror repos on failed async clones (silent failure)
- Discovered
service: "git"fails for private repos;service: "github"with valid token works - Mirror interval set to 1 hour, manual sync available via API
- Rewrote MIRRORING-GUIDE.md with pull mirror approach and all gotchas
- Updated HANDOVER.md and lessons-learned.md
Session 4: 1Password Token Update and Task Tracking
- Attempted to update
github.personal.tokenviaop item edit-- blocked by service account read-only permissions - Confirmed
op_zesticai_non_prod.shsources a SERVICE_ACCOUNT with no write access to TerraphimPlatform vault - Created GitHub issue to track the fix: https://github.com/AlexMikhalev/cto-executive-system/issues/8
All Commits (pushed to GitHub, auto-synced to Gitea)
57eabbbrefactor: Replace GitHub Actions mirroring with Gitea native pull mirror0e69456docs: Update handover with articles, publishing, and new lessons learned62e2275docs: Add articles comparing beads/br/agent-mail with Gitea replacement4ff1821docs: Update handover and add lessons learnedb67284echore: Fix trailing whitespace across project files66b305dfeat(gitea-skill): Add integration tests and fix workflow label swap1fd202dfeat(gitea-skill): Rewrite Gitea agent skill with verified API patterns
Next Steps
- Update
github.personal.tokenin 1Password manually (service account is read-only) -- tracked in https://github.com/AlexMikhalev/cto-executive-system/issues/8 - Grant write access to
op_zesticai_non_prodservice account on TerraphimPlatform vault - Consider adding gitea-mcp or forgejo-mcp as MCP server for Claude Code (see SKILL.md MCP Integration section)
- Create first real milestone and tasks in
terraphim/agent-tasksfor a project
Contacts
- Infrastructure owner: Alex
- 1Password vault: TerraphimPlatform