GitTinkerer Service (VPS HTTP API)¶
This folder contains a lightweight HTTP service (Node 24) used by the Svelte web UI. The service exposes REST endpoints for run creation, status polling, analytics aggregation, and admin operations.
Quick Links: - API Reference — Complete endpoint documentation with examples - Development Guide — Setup, testing, and debugging instructions - Architecture Overview — System context and data flow diagrams
Architecture¶
The service follows a Clean Architecture pattern with clear separation of concerns:
graph TD
Controllers["🎯 Controllers"]
UseCases["⚙️ Use Cases"]
Domain["🔶 Domain"]
Infra["🔌 Infrastructure"]
Controllers -->|orchestrate| UseCases
UseCases -->|enforce rules| Domain
UseCases -->|delegate to| Infra
Infra -->|Postgres| DB["Database"]
Infra -->|Redis| Cache["Cache & Rate Limits"]
Infra -->|GitHub API| GitHub["GitHub"]
Infra -->|Spawn Process| Process["Bash Runner"]
Infra -->|Filesystem| FS["Artifacts"]
Infra -->|Sentry| Monitoring["Error Tracking"]
style Controllers fill:#4A90E2
style UseCases fill:#F5A623
style Domain fill:#7ED321
style Infra fill:#BD10E0
Layer Responsibilities¶
- Controllers (
src/controllers/) - HTTP request/response handling via Fastify
- Input validation and error translation
-
Dependency wiring (see Dependency Injection below)
-
Use Cases (
src/usecases/) - Application logic orchestration
- Business rule enforcement (run creation, rate limiting, pause checks)
-
Coordination between domain and infrastructure
-
Domain (
src/domain/) - Pure entities (
Run,RepoRef,Instruction, etc.) - Validation rules and value objects
-
No external dependencies
-
Infrastructure (
src/infra/) - External service adapters (Postgres, Redis, GitHub, filesystem, process spawning)
- Database repositories and queries
- Cache and rate limit management
Dependency Injection Pattern¶
The service uses function-based dependency injection to wire dependencies between layers. Each controller creates a dependency object via factory functions in src/controllers/dependencies.ts:
// Example: Assembling RunController dependencies
function createRunControllerDeps(pool: Pool): RunControllerDependencies {
return {
// Injected use cases
createRun,
startRun,
getRun,
listRuns,
// Injected repositories and external service adapters
startRunDeps: {
getRun: getRunRepo, // Database adapter
updateRunStatus, // Database adapter
spawnGittinkerer, // Process spawning adapter
cacheRunStatus: (...) => cacheRunStatus(...), // Redis adapter
},
// Nested dependency objects follow a flat-then-nested pattern
getRunDeps: {
getRun: getRunRepo,
getCachedRunStatus, // Redis cache
readArtifacts, // Filesystem adapter
},
};
}
Benefits: - Testability: Swap real adapters for mocks in tests without touching business logic - Clarity: All dependencies explicit in one place per controller - Flexibility: Change implementations (e.g., Redis → in-memory cache) at the factory level
Database Architecture¶
graph TD
subgraph "Application Layer"
Server[server.ts]
UseCases[Use Cases]
end
subgraph "Infrastructure Layer - Database"
DBIndex[db/index.ts\\nPool + withClient]
Migrations[db/migrations.ts\\nSchema Versions]
Types[db/types.ts\\nInterfaces + Zod]
subgraph "Repositories"
RunsRepo[db/runsRepo.ts\\nCRUD for runs]
MetricsRepo[db/metricsRepo.ts\\nMetrics operations]
PausedRepo[db/pausedReposRepo.ts\\nPause management]
RateLimitRepo[db/rateLimitsRepo.ts\\nRate limiting]
end
end
subgraph "PostgreSQL"
DB[(Database\\nruns, run_metrics,\\nrate_limits, paused_repos,\\npaused_run_targets)]
end
Server -->|initializeDatabase| DBIndex
UseCases -->|withClient| DBIndex
DBIndex -->|migrate| Migrations
DBIndex -->|exports| RunsRepo
DBIndex -->|exports| MetricsRepo
DBIndex -->|exports| PausedRepo
DBIndex -->|exports| RateLimitRepo
RunsRepo -->|queries| DB
MetricsRepo -->|queries| DB
PausedRepo -->|queries| DB
RateLimitRepo -->|queries| DB
Migrations -->|DDL| DB
Types -.->|types| RunsRepo
Types -.->|types| MetricsRepo
Types -.->|types| PausedRepo
Types -.->|types| RateLimitRepo
style DBIndex fill:#f9f,stroke:#333,stroke-width:3px
style Types fill:#bbf,stroke:#333
style DB fill:#dfd,stroke:#333
File Structure¶
service/src/infra/db/
├── index.ts # Pool, withClient, exports
├── types.ts # TypeScript interfaces + Zod schemas
├── migrations.ts # Migration definitions
├── runsRepo.ts # Runs table operations
├── metricsRepo.ts # Metrics table operations
├── pausedReposRepo.ts # Paused repos/targets operations
└── rateLimitsRepo.ts # Rate limits operations
Type System Architecture¶
graph TD
subgraph Domain["Domain Layer (src/domain/)"]
Run[Run Entity\\n- Immutable\\n- State transitions\\n- Business logic]
RunStatus[RunStatus Enum\\n- Type-safe statuses\\n- Converters]
RunErrors[RunErrors\\n- Typed exceptions\\n- Stage tracking]
RepoRef[RepoRef\\n- Repo identity\\n- Validation]
Instruction[Instruction\\n- Comment parsing\\n- Prompt building]
end
subgraph Types["Types Layer (src/types/)"]
GitHub[GitHub Types\\n- Webhook payloads\\n- Zod schemas]
Payload[Payload Types\\n- CLI payload\\n- Validation]
API[API Types\\n- Request/Response\\n- Zod schemas]
end
subgraph Infra["Infrastructure (src/infra/)"]
DB[(Database\\nRun, RunMetric)]
GitHubAPI[GitHub API\\nClient]
Process[Process\\nSpawner]
end
Run --> RunStatus
Run --> RunErrors
Run --> RepoRef
Run --> Instruction
Payload --> RepoRef
Payload --> Instruction
API --> Run
API --> Payload
GitHub --> API
DB -.->|maps to| Run
GitHubAPI -.->|uses| GitHub
Process -.->|uses| Payload
style Domain fill:#e1f5e1
style Types fill:#e1e5f5
style Infra fill:#f5e1e1
File Structure¶
service/src/
├── domain/
│ ├── run/
│ │ ├── Run.ts # Domain entity with business logic
│ │ ├── RunStatus.ts # Enum and converters
│ │ ├── RunErrors.ts # Typed error classes
│ │ └── index.ts # Re-exports
│ ├── repo/
│ │ ├── RepoRef.ts # Repository reference model
│ │ └── index.ts
│ └── instruction/
│ ├── Instruction.ts # Instruction/comment model
│ └── index.ts
└── types/
├── github.ts # GitHub webhook types + Zod schemas
├── payload.ts # CLI payload types + Zod schemas
├── api.ts # API request/response types + Zod schemas
├── guards.ts # Type guard utilities
└── index.ts # Re-exports
API Quick Reference¶
For complete API documentation with request/response examples, see API_REFERENCE.md.
POST /api/runs— Create a new runGET /api/runs/:run_id— Get run status and artifactsGET /api/runs?page=1&limit=20— List runs with paginationPOST /api/github/webhook— GitHub webhook delivery handlerGET /api/analytics— Aggregate usage metricsPOST /api/admin/pause-repo— Admin: pause a repositoryGET /health— Health check endpoint
Testing¶
Test Suites¶
The test suite is organized into unit and integration layers:
- Unit tests (
npm run test:unit) - Target domain entities and use cases
- Fast, isolated, no external dependencies
-
Mock repositories and external adapters
-
Integration tests (
npm run test:integration) - Cover infrastructure adapters (Postgres/Redis) and HTTP routes
- Exercise end-to-end flows: request → controller → use case → repository
- Slower than unit tests; require real database/cache instances
-
Lifecycle tests in
src/__tests__/integration/exercise server/app bootstrap, health checks, and shutdown paths -
Full test run:
npm run test:coverage(runs all tests + coverage report) - Interactive UI:
npm run test:ui(opens test explorer)
Coverage Targets¶
All tests must meet the following coverage thresholds (enforced by Vitest):
| Metric | Target |
|---|---|
| Line Coverage | 80% |
| Function Coverage | 75% |
| Statement Coverage | 80% |
| Branch Coverage | 70% |
Test Helpers & Mocks¶
Reusable test utilities live in src/__tests__/helpers/:
- Factories: Create test domain objects (
Run,RepoRef, etc.) - Database mocks: In-memory pool and client for unit tests; real Postgres for integration tests
- Redis mocks: Stub Redis client by default; set
REDIS_URLto point at a real instance for integration testing - GitHub API mocks: Mock GitHub API client for webhook and PR fetch scenarios
- Process spawning mocks: Mock
bin/gittinkererexecution - Filesystem mocks: Mock artifact creation and reading
- Sentry mock: Stub error tracking for tests
- Signal handlers: Mock process signals for lifecycle tests
Test Database Setup¶
- Set
DATABASE_URL_TESTto a reachable Postgres instance - Migrations run automatically in test helpers (see
src/__tests__/integration/setup.ts) - Database is cleaned between test suites
Local Development¶
For comprehensive setup instructions, see DEVELOPMENT.md.
Configuration¶
- GitHub App private key: Store as
service/private-key.pem(service prefers this, falls back toGH_APP_PRIVATE_KEYenv var) - Environment variables: Automatically loaded from
service/.envviadotenv(copy from root.env.example) - TypeScript compilation: Runtime entrypoint is
src/server.ts(compiled todist/server.js)
Known Limitations¶
These are tracked in TODO.md: