Skip to content

Git Connector

Git Connector (Express) synchronizes Strapi organization and user events to Gitea (git hosting), creating repositories, managing permissions, and syncing SSH keys.

  • Port: 3002
  • Tech: Express, TypeScript, Axios
  • Messaging: RabbitMQ (git.events exchange)
  • Target: Gitea Toolbox (9023) + Gitea Apps (9034)
  • Logging: Winston

Git Connector bridges Strapi events and Gitea:

  1. User Created → Create Gitea user
  2. Organization Created → Create Gitea organization
  3. Member Added → Add user to org in Gitea
  4. SSH Key Changed → Update Gitea SSH keys
  5. Repository Sync → Mirror templates from Toolbox to Apps
InstancePortPurposeDatabase
Toolbox9023System repos, templates, shared toolsShared MySQL (3308)
Apps9034User/org repos, applicationsShared MySQL (3308)
SSH9022 (Toolbox), 9033 (Apps)Git over SSH

Git Connector manages both instances with separate API calls.

Strapi event (org.create)
RabbitMQ: org.events (routing key: create)
Git Connector consumes event
├─ Extract organization data
├─ Create org in Toolbox (POST /api/v1/orgs)
├─ Create org in Apps (POST /api/v1/orgs)
└─ Sync members (add users to org in both instances)
Webhook back to Strapi (optional, for audit)
Frontend sees organization ready for Git operations
EventAction
org.createCreate Gitea org in Toolbox + Apps
org.updateUpdate Gitea org name/description
org.deleteDelete Gitea org (soft delete if repos exist)
EventAction
org.member.addedAdd user to Gitea org
org.member.removedRemove user from Gitea org
org.member.role.changedUpdate Gitea team (admin/member)
EventAction
user.createCreate Gitea user
user.updateUpdate username/email
user.ssh-key.addedAdd SSH public key to Gitea
user.ssh-key.removedRemove SSH key from Gitea

.env variables:

Terminal window
# Gitea instances
GITEA_TOOLBOX_URL=http://gitea-toolbox:3000
GITEA_TOOLBOX_ADMIN_TOKEN=<token>
GITEA_APPS_URL=http://gitea-apps:3000
GITEA_APPS_ADMIN_TOKEN=<token>
# RabbitMQ
RABBITMQ_URL=amqp://admin:admin@sencai-mq:5672
# Port
PORT=3002
# Logging
LOG_LEVEL=info

To create admin token:

  1. SSH into Gitea container: docker exec -it gitea-toolbox bash
  2. Run: gitea admin user change-password admin --password <newpass>
  3. Login to Gitea UI: http://localhost:9023
  4. Settings → Applications → Generate new token
  5. Copy token and set in .env

Or use Gitea CLI:

Terminal window
docker exec gitea-toolbox gitea admin user change-password admin --password admin
docker exec gitea-toolbox gitea admin user generate-access-token -u admin -n sencai

POST /org/provision creates a Gitea organisation in the Apps instance and registers the necessary team structure. It does not create any default repositories — no policies, config, or infrastructure repos are created automatically. Repository creation is always an explicit user action via the frontend or a direct API call.

All endpoints are served on port 3002. Endpoints under /org/ require the X-Service-Secret header. For the complete endpoint list, see git-connector/CLAUDE.md.

MethodPathDescription
GET/healthHealth check — Gitea Toolbox + Apps connectivity
GET/statusInstance status and statistics
POST/syncManual sync trigger
GET/queue/statsRabbitMQ queue statistics
POST/cleanup/duplicatesRemove duplicate repository records in Strapi
POST/webhooks/repositoryGitea inbound webhook (requires WEBHOOK_ENABLED)
MethodPathDescription
POST/org/provisionCreate Gitea organisation (no default repos created)
DELETE/org/:slug/teardownArchive organisation — all repos go read-only
GET/org/:slug/reposList organisation repositories
POST/org/:slug/reposCreate a new repository
GET/org/:slug/packagesList packages (container, helm, generic, npm, pypi)
GET/org/:slug/ciList workflow runs (Gitea Actions API)
GET/org/:slug/mirrorsList mirror repositories
POST/org/:slug/mirrorsCreate a mirror repository
DELETE/org/:slug/mirrors/:repoNameDelete a mirror repository
POST/org/:slug/tokensCreate personal access token for an org member
MethodPathDescription
GET/org/:slug/repo/:repoRepository detail
DELETE/org/:slug/repo/:repoDelete repository
PUT/org/:slug/repo/:repoUpdate repository settings (name, description, private, has_issues, website)
GET/org/:slug/repo/:repo/branchesList branches
GET/org/:slug/repo/:repo/commitsRecent commits (query: limit, max 50)
GET/org/:slug/repo/:repo/issuesList issues (query: state, limit)
MethodPathDescription
GET/org/:slug/repo/:repo/webhooksList repository webhooks
POST/org/:slug/repo/:repo/webhooksCreate webhook (body: url, secret, events, active)
DELETE/org/:slug/repo/:repo/webhooks/:idDelete webhook
MethodPathDescription
GET/org/:slug/repo/:repo/keysList deploy keys
POST/org/:slug/repo/:repo/keysAdd deploy key (body: title, key, read_only)
DELETE/org/:slug/repo/:repo/keys/:idDelete deploy key
MethodPathDescription
GET/org/:slug/repo/:repo/secretsList Actions secrets
PUT/org/:slug/repo/:repo/secrets/:nameCreate or update secret (body: data)
DELETE/org/:slug/repo/:repo/secrets/:nameDelete secret
GET/org/:slug/repo/:repo/variablesList Actions variables
PUT/org/:slug/repo/:repo/variables/:nameCreate or update variable (body: value)
DELETE/org/:slug/repo/:repo/variables/:nameDelete variable
MethodPathDescription
GET/org/:slug/repo/:repo/treeDirectory tree (query: path, ref — default HEAD)
GET/org/:slug/repo/:repo/contentRaw file content (query: path required, ref)

Gitea Actions is enabled via the [actions] section in app.ini:

[actions]
ENABLED = true
WORKFLOW_DIRS = .sencai/workflows,.gitea/workflows,.github/workflows

The .sencai/workflows/ directory is checked first — it is the recommended location for Sencai-native workflow files. The .gitea/workflows/ and .github/workflows/ directories are supported for compatibility with existing pipelines.

Limitations:

  • Mirror repositories do not trigger Actions. When a mirror is synced, Gitea does not generate a push event, so on: push workflows will not fire. Use on: schedule or on: workflow_dispatch instead.
  • Runners must be registered before any workflow can execute. Queued runs wait indefinitely until a matching runner connects.
Terminal window
GET /health
Response:
{
"status": "up",
"gitea": {
"toolbox": "connected",
"apps": "connected"
}
}
Terminal window
POST /api/webhook (internal, no direct access needed)
Terminal window
POST /api/sync/:organisationId
Response:
{
"status": "synced",
"organisation": "org-name",
"toolbox": "created",
"apps": "created"
}

Git Connector calls Gitea API (v1):

Terminal window
POST http://gitea-toolbox:3000/api/v1/orgs
Authorization: token <ADMIN_TOKEN>
{
"username": "org-name",
"full_name": "Organization Name",
"description": "Org description",
"website": "https://sencai.space",
"location": "Remote"
}
Terminal window
POST http://gitea-toolbox:3000/api/v1/orgs/org-name/teams
Authorization: token <ADMIN_TOKEN>
{
"name": "Developers",
"permission": "write", # or "read", "admin"
"includes_all_repositories": true
}
Terminal window
POST http://gitea-toolbox:3000/api/v1/user/keys
Authorization: token <USER_TOKEN>
{
"title": "My SSH Key",
"key": "ssh-rsa AAAAB3...",
"read_only": false
}

Git Connector consumes from git.events and org.events exchanges.

Example event payload:

{
"event": "org.create",
"organisation": {
"id": 1,
"name": "acme-corp",
"description": "Acme AI Team",
"ownerId": 10
},
"timestamp": "2024-01-01T12:00:00Z"
}
Terminal window
# View logs
docker logs -f git-connector
# Example output:
2024-01-01T12:00:00Z [INFO] Received event: org.create
2024-01-01T12:00:00Z [INFO] Creating Gitea org: acme-corp in Toolbox
2024-01-01T12:00:01Z [INFO] Creating Gitea org: acme-corp in Apps
2024-01-01T12:00:02Z [INFO] Organization synced successfully

Q: “Gitea connection refused”

A: Gitea not running or not accessible:

Terminal window
docker logs gitea-toolbox
docker logs gitea-apps
# Ensure git profile is running: ./run-local.sh git

Q: “Invalid admin token”

A: Token is expired or incorrect. Generate new token in Gitea UI (Settings → Applications).

Q: Organization not created in Gitea

A: Check Git Connector logs for errors. Verify:

  1. RabbitMQ is running: docker logs sencai-mq
  2. Event is published: Check RabbitMQ UI (localhost:15672)
  3. Git Connector is consuming: docker logs git-connector

Q: SSH keys not syncing

A: Verify:

  • SSH key format is valid (starts with ssh-rsa or ssh-ed25519)
  • User exists in Gitea
  • User token has write permission

Q: “User already exists” error

A: User might already exist in Gitea from previous sync. Idempotency check may need improvement.

Q: Can’t push to Gitea repo via SSH

A: Verify:

  • SSH key is added to Gitea user
  • SSH port (9022 for Toolbox, 9033 for Apps) is open
  • Git URL uses correct format: git@localhost:orgname/reponame.git