docs: initialize stub-service repo with ADR-001
Architecture Decision Record for a Go-based HTTP stub/mock service (Mountebank alternative) with Web UI and LLM-powered config generation. Covers: component architecture, technology choices (Go + chi + Templ/htmx + SQLite), API design, data model, deployment model.
This commit is contained in:
commit
df64b7f89c
|
|
@ -0,0 +1,36 @@
|
|||
# Binaries
|
||||
stub-service
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Build output
|
||||
/bin/
|
||||
/dist/
|
||||
|
||||
# Test
|
||||
*.test
|
||||
*.out
|
||||
coverage.html
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Data
|
||||
/data/
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# stub-service
|
||||
|
||||
Go-based HTTP stub/mock service with Web UI and LLM-powered configuration generation.
|
||||
|
||||
An alternative to [Mountebank](http://www.mbtest.org/) focused on simplicity, a modern web interface, and AI-assisted stub creation.
|
||||
|
||||
## Status
|
||||
|
||||
**Planning phase** — see [ADR-001](docs/adr/adr-001.md) for the architecture decision record.
|
||||
|
||||
## Features (planned)
|
||||
|
||||
- HTTP/HTTPS stub server with rich request matching (equals, contains, regex, JSONPath, XPath)
|
||||
- REST API for full CRUD management of imposters and stubs
|
||||
- Web UI for browsing, creating, and editing mock configurations
|
||||
- Real-time request log viewer
|
||||
- LLM integration for natural-language stub generation
|
||||
- Proxy mode with record/replay
|
||||
- Response templating via Go templates
|
||||
- File-based or embedded database persistence
|
||||
- Docker-ready deployment
|
||||
|
||||
## License
|
||||
|
||||
Internal use.
|
||||
|
|
@ -0,0 +1,531 @@
|
|||
# ADR-001: Architecture of stub-service — Go-based HTTP Stub/Mock Service
|
||||
|
||||
**Status:** Proposed
|
||||
**Date:** 2026-05-07
|
||||
**Authors:** backend-agent
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Our development and QA workflows require a service-virtualization tool that can stand in for real HTTP services during integration testing, local development, and demo environments. [Mountebank](http://www.mbtest.org/) is the de-facto open-source option, but it presents several pain points for our team:
|
||||
|
||||
1. **Node.js runtime** — adds a runtime dependency our Go/Java-centric infrastructure doesn't otherwise need.
|
||||
2. **No built-in Web UI** — managing imposters requires curl/Postman or third-party UIs.
|
||||
3. **No AI-assisted authoring** — creating complex stubs is manual and error-prone.
|
||||
4. **Limited extensibility** — behaviours and predicates are plugin-based in JavaScript, which is awkward to extend from our stack.
|
||||
|
||||
We need a lightweight, single-binary alternative written in Go that keeps Mountebank's core concepts (imposters, stubs, predicates, responses, proxies) while adding a web UI and LLM-powered stub generation.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
Build **stub-service** — a Go application that provides:
|
||||
|
||||
- An HTTP stub server with rich request matching
|
||||
- A REST management API (CRUD for imposters/stubs)
|
||||
- A web UI for visual management and real-time request logging
|
||||
- An LLM adapter for natural-language stub authoring
|
||||
- Proxy mode with record/replay capability
|
||||
|
||||
### High-Level Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ stub-service process │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
|
||||
│ │ Management │ │ Web UI │ │ Stub Listeners │ │
|
||||
│ │ REST API │ │ (embedded) │ │ (dynamic ports) │ │
|
||||
│ │ :8080/api/v1 │ │ :8080/ui │ │ :N per imposter │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └───────────┬────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Engine │ │
|
||||
│ │ ┌─────────────┐ ┌──────────┐ ┌───────────┐ ┌────────┐ │ │
|
||||
│ │ │ Imposter │ │ Matcher │ │ Response │ │ Proxy │ │ │
|
||||
│ │ │ Manager │ │ Engine │ │ Renderer │ │ Module │ │ │
|
||||
│ │ └─────────────┘ └──────────┘ └───────────┘ └────────┘ │ │
|
||||
│ └──────────────────────────┬──────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────┼────────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
|
||||
│ │ Stub Store │ │ Request Log │ │ LLM Adapter │ │
|
||||
│ │ (SQLite / │ │ (ring buf) │ │ (Anthropic / │ │
|
||||
│ │ bbolt) │ │ │ │ OpenAI) │ │
|
||||
│ └─────────────┘ └──────────────┘ └─────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Component Breakdown
|
||||
|
||||
#### 1. Management REST API (`internal/api`)
|
||||
|
||||
The control plane. All imposter/stub CRUD operations happen here.
|
||||
|
||||
- Listens on a single admin port (default `:8080`), path prefix `/api/v1`
|
||||
- JSON request/response bodies
|
||||
- Endpoints (see API Design section below)
|
||||
- Provides SSE endpoint for real-time request log streaming
|
||||
|
||||
#### 2. Web UI (`internal/ui`)
|
||||
|
||||
An embedded single-page application served from the same admin port under `/ui`.
|
||||
|
||||
- **Technology:** [Templ](https://templ.guide/) templates + [htmx](https://htmx.org/) + minimal CSS (e.g., Pico CSS or similar classless framework)
|
||||
- **Rationale:** No JavaScript build step, no npm dependency. Templ compiles to Go, htmx provides dynamic behavior via HTML attributes. The result is a server-rendered UI that feels interactive without a JS framework.
|
||||
- **Pages:**
|
||||
- Dashboard — list of all imposters with status, port, hit counts
|
||||
- Imposter detail — list of stubs, predicates, responses, recent requests
|
||||
- Stub editor — form-based creation/editing of stubs with predicate/response builders
|
||||
- Request log — real-time log of incoming requests (SSE-powered)
|
||||
- LLM assistant — text input for natural-language stub creation
|
||||
|
||||
#### 3. Stub Listeners (`internal/server`)
|
||||
|
||||
Each imposter binds to a dedicated port and handles incoming traffic.
|
||||
|
||||
- Dynamically started/stopped when imposters are created/deleted
|
||||
- Each listener runs an HTTP handler that delegates to the Matcher Engine
|
||||
- TLS support via optional per-imposter cert/key configuration
|
||||
|
||||
#### 4. Core Engine (`internal/engine`)
|
||||
|
||||
The domain logic of the application.
|
||||
|
||||
- **Imposter Manager** — lifecycle management of imposters (create, update, delete, start, stop). Coordinates listener creation and stub store persistence.
|
||||
- **Matcher Engine** — evaluates incoming requests against stub predicates. Returns the first matching stub or a configurable default response. Supports predicate composition (`and`, `or`, `not`).
|
||||
- **Response Renderer** — produces the HTTP response from a stub definition. Handles static bodies, Go template rendering (with request data as context), latency injection, and header manipulation.
|
||||
- **Proxy Module** — forwards requests to a real upstream, optionally records the response as a new stub for future replay.
|
||||
|
||||
#### 5. Stub Store (`internal/store`)
|
||||
|
||||
Persistence layer for imposters, stubs, and request logs.
|
||||
|
||||
- **Primary:** SQLite via [modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite) (pure Go, no CGO) or [bbolt](https://github.com/etcd-io/bbolt) for a simpler key-value approach.
|
||||
- **Decision:** SQLite preferred — it supports rich queries for filtering stubs and request logs, and the schema maps naturally to the relational data model.
|
||||
- Migrations managed via embedded SQL files (`embed` package).
|
||||
- Backup: the SQLite file can be copied while the service runs (WAL mode).
|
||||
- Optional file-based export/import in JSON format for portability.
|
||||
|
||||
#### 6. LLM Adapter (`internal/llm`)
|
||||
|
||||
Translates natural-language descriptions into stub configurations.
|
||||
|
||||
- **Interface:** A `StubGenerator` interface that takes a text prompt and returns a structured stub definition.
|
||||
- **Implementation:** HTTP client calling the Anthropic Messages API (Claude) or OpenAI Chat Completions API, configurable via environment variables.
|
||||
- **Prompt strategy:** System prompt contains the stub JSON schema and examples. User message is the natural-language description. The LLM returns a JSON stub definition that is validated before being applied.
|
||||
- **Flow:**
|
||||
1. User types: *"Return 200 with a JSON user profile when GET /api/users/123 is called"*
|
||||
2. LLM adapter sends the prompt with schema context
|
||||
3. Response is parsed and validated against the stub schema
|
||||
4. If valid, the stub is presented to the user for confirmation in the Web UI
|
||||
5. On confirmation, the stub is saved via the Management API
|
||||
|
||||
#### 7. Request Log (`internal/log`)
|
||||
|
||||
In-memory ring buffer of recent requests per imposter, with optional persistence to SQLite.
|
||||
|
||||
- Stores: timestamp, method, path, headers, body (truncated), matched stub ID, response status, latency
|
||||
- Queryable via Management API (filter by imposter, time range, match status)
|
||||
- Streamed to the Web UI via SSE
|
||||
|
||||
---
|
||||
|
||||
### Technology Choices
|
||||
|
||||
| Concern | Choice | Rationale |
|
||||
|---|---|---|
|
||||
| Language | Go 1.23+ | Single binary, excellent HTTP stdlib, team expertise |
|
||||
| HTTP framework | `net/http` (stdlib) + [chi](https://github.com/go-chi/chi) router | Minimal dependency, chi adds route params and middleware without abstracting stdlib |
|
||||
| Web UI | Templ + htmx + Pico CSS | No JS build toolchain, server-rendered, interactive via htmx |
|
||||
| Database | SQLite (modernc.org/sqlite) | Pure Go, zero CGO, rich SQL queries, single file |
|
||||
| LLM client | Direct HTTP (Anthropic SDK or `net/http`) | Avoid heavy SDK deps; a thin wrapper suffices |
|
||||
| Config | Environment variables + optional YAML file | 12-factor, simple for Docker |
|
||||
| Logging | `log/slog` (stdlib) | Structured logging, zero deps |
|
||||
| Testing | `testing` + [testify](https://github.com/stretchr/testify) | Assertions, standard tooling |
|
||||
| TLS | `crypto/tls` (stdlib) | Per-imposter certs for HTTPS stubs |
|
||||
|
||||
---
|
||||
|
||||
### API Design Overview
|
||||
|
||||
Base URL: `http://localhost:8080/api/v1`
|
||||
|
||||
#### Imposters
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `GET` | `/imposters` | List all imposters |
|
||||
| `POST` | `/imposters` | Create a new imposter |
|
||||
| `GET` | `/imposters/{id}` | Get imposter details |
|
||||
| `PUT` | `/imposters/{id}` | Update imposter |
|
||||
| `DELETE` | `/imposters/{id}` | Delete imposter and stop listener |
|
||||
| `DELETE` | `/imposters` | Delete all imposters |
|
||||
| `POST` | `/imposters/{id}/start` | Start the stub listener |
|
||||
| `POST` | `/imposters/{id}/stop` | Stop the stub listener |
|
||||
|
||||
#### Stubs (nested under imposter)
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `GET` | `/imposters/{id}/stubs` | List stubs for an imposter |
|
||||
| `POST` | `/imposters/{id}/stubs` | Add a stub |
|
||||
| `PUT` | `/imposters/{id}/stubs/{stubId}` | Update a stub |
|
||||
| `DELETE` | `/imposters/{id}/stubs/{stubId}` | Delete a stub |
|
||||
|
||||
#### Request Log
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `GET` | `/imposters/{id}/requests` | Get request log (paginated) |
|
||||
| `GET` | `/imposters/{id}/requests/stream` | SSE stream of live requests |
|
||||
| `DELETE` | `/imposters/{id}/requests` | Clear request log |
|
||||
|
||||
#### LLM
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `POST` | `/generate` | Generate stub config from natural-language prompt |
|
||||
|
||||
#### System
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `GET` | `/health` | Health check |
|
||||
| `GET` | `/config` | Current configuration (redacted) |
|
||||
| `POST` | `/import` | Import imposters from JSON file |
|
||||
| `GET` | `/export` | Export all imposters as JSON |
|
||||
|
||||
---
|
||||
|
||||
### Data Model
|
||||
|
||||
#### Imposter
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "User Service Mock",
|
||||
"protocol": "http",
|
||||
"port": 4545,
|
||||
"tls": {
|
||||
"cert": "...",
|
||||
"key": "..."
|
||||
},
|
||||
"default_response": {
|
||||
"status": 404,
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"body": "{\"error\": \"no matching stub\"}"
|
||||
},
|
||||
"stubs": [ ... ],
|
||||
"state": "running",
|
||||
"created_at": "2026-05-07T10:00:00Z",
|
||||
"request_count": 142
|
||||
}
|
||||
```
|
||||
|
||||
#### Stub
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"imposter_id": "uuid",
|
||||
"priority": 1,
|
||||
"predicates": [ ... ],
|
||||
"response": { ... },
|
||||
"hit_count": 37,
|
||||
"created_at": "2026-05-07T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Predicate
|
||||
|
||||
Predicates are composable. Each predicate has a `type` and type-specific fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "equals",
|
||||
"field": "path",
|
||||
"value": "/api/users/123"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "regex",
|
||||
"field": "path",
|
||||
"value": "^/api/users/\\d+$"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "jsonpath",
|
||||
"expression": "$.order.items[?(@.quantity > 5)]",
|
||||
"exists": true
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "and",
|
||||
"predicates": [
|
||||
{"type": "equals", "field": "method", "value": "POST"},
|
||||
{"type": "contains", "field": "body", "value": "\"action\":\"create\""}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Supported predicate types:
|
||||
- `equals` — exact match on method, path, header, query param, or body
|
||||
- `contains` — substring match
|
||||
- `regex` — regular expression match
|
||||
- `jsonpath` — JSONPath expression evaluation
|
||||
- `xpath` — XPath expression evaluation (for XML bodies)
|
||||
- `and` — all child predicates must match
|
||||
- `or` — at least one child predicate must match
|
||||
- `not` — invert a child predicate
|
||||
|
||||
Fields available for matching: `method`, `path`, `query.<param>`, `header.<name>`, `body`
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"body": "{\"id\": 123, \"name\": \"Alice\"}",
|
||||
"template": false,
|
||||
"latency_ms": 0,
|
||||
"proxy": null
|
||||
}
|
||||
```
|
||||
|
||||
When `template: true`, the `body` is rendered as a Go template with the request context:
|
||||
|
||||
```
|
||||
{
|
||||
"echo": "You requested {{.Request.Path}} with method {{.Request.Method}}"
|
||||
}
|
||||
```
|
||||
|
||||
#### Proxy Response
|
||||
|
||||
```json
|
||||
{
|
||||
"proxy": {
|
||||
"to": "https://real-service.example.com",
|
||||
"mode": "record",
|
||||
"add_wait_behavior": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Proxy modes:
|
||||
- `passthrough` — forward and return, do not record
|
||||
- `record` — forward, return, and save as a new stub for future replay
|
||||
- `replay` — use previously recorded response if available, otherwise forward
|
||||
|
||||
---
|
||||
|
||||
### SQLite Schema (initial)
|
||||
|
||||
```sql
|
||||
CREATE TABLE imposters (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
protocol TEXT NOT NULL DEFAULT 'http',
|
||||
port INTEGER NOT NULL UNIQUE,
|
||||
tls_cert TEXT,
|
||||
tls_key TEXT,
|
||||
default_response_json TEXT,
|
||||
state TEXT NOT NULL DEFAULT 'stopped',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE stubs (
|
||||
id TEXT PRIMARY KEY,
|
||||
imposter_id TEXT NOT NULL REFERENCES imposters(id) ON DELETE CASCADE,
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
predicates_json TEXT NOT NULL,
|
||||
response_json TEXT NOT NULL,
|
||||
hit_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_stubs_imposter ON stubs(imposter_id, priority);
|
||||
|
||||
CREATE TABLE request_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
imposter_id TEXT NOT NULL REFERENCES imposters(id) ON DELETE CASCADE,
|
||||
timestamp TEXT NOT NULL,
|
||||
method TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
headers_json TEXT,
|
||||
body TEXT,
|
||||
matched_stub_id TEXT,
|
||||
response_status INTEGER,
|
||||
latency_ms INTEGER,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_request_log_imposter_ts ON request_log(imposter_id, timestamp DESC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Project Layout
|
||||
|
||||
```
|
||||
stub-service/
|
||||
├── cmd/
|
||||
│ └── stub-service/
|
||||
│ └── main.go # Entry point, config loading, wiring
|
||||
├── internal/
|
||||
│ ├── api/ # Management REST API handlers
|
||||
│ │ ├── handler.go
|
||||
│ │ ├── imposter.go
|
||||
│ │ ├── stub.go
|
||||
│ │ ├── requestlog.go
|
||||
│ │ ├── generate.go # LLM generation endpoint
|
||||
│ │ └── middleware.go
|
||||
│ ├── engine/ # Core domain logic
|
||||
│ │ ├── imposter.go # Imposter manager
|
||||
│ │ ├── matcher.go # Predicate matching engine
|
||||
│ │ ├── renderer.go # Response rendering (static, template)
|
||||
│ │ └── proxy.go # Proxy/record/replay
|
||||
│ ├── model/ # Domain types
|
||||
│ │ ├── imposter.go
|
||||
│ │ ├── stub.go
|
||||
│ │ ├── predicate.go
|
||||
│ │ └── response.go
|
||||
│ ├── store/ # Persistence
|
||||
│ │ ├── store.go # Store interface
|
||||
│ │ ├── sqlite.go # SQLite implementation
|
||||
│ │ └── migrations/ # Embedded SQL migrations
|
||||
│ │ └── 001_init.sql
|
||||
│ ├── server/ # Dynamic stub HTTP listeners
|
||||
│ │ └── listener.go
|
||||
│ ├── llm/ # LLM integration
|
||||
│ │ ├── generator.go # StubGenerator interface
|
||||
│ │ ├── anthropic.go # Anthropic/Claude implementation
|
||||
│ │ └── openai.go # OpenAI implementation
|
||||
│ ├── ui/ # Web UI
|
||||
│ │ ├── templates/ # Templ files
|
||||
│ │ ├── static/ # CSS, minimal JS (htmx)
|
||||
│ │ └── handler.go # UI route handlers
|
||||
│ └── config/ # Configuration loading
|
||||
│ └── config.go
|
||||
├── docs/
|
||||
│ └── adr/
|
||||
│ └── adr-001.md # This document
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── Dockerfile
|
||||
├── docker-compose.yaml
|
||||
├── Makefile
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Deployment Model
|
||||
|
||||
**Docker (primary):**
|
||||
|
||||
```dockerfile
|
||||
# Build stage
|
||||
FROM golang:1.23-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -o /stub-service ./cmd/stub-service
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache ca-certificates
|
||||
COPY --from=build /stub-service /usr/local/bin/stub-service
|
||||
EXPOSE 8080
|
||||
VOLUME /data
|
||||
ENTRYPOINT ["stub-service"]
|
||||
```
|
||||
|
||||
**docker-compose.yaml (local dev):**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
stub-service:
|
||||
build: .
|
||||
ports:
|
||||
- "8080:8080" # Admin API + Web UI
|
||||
- "4545-4600:4545-4600" # Range for stub listeners
|
||||
volumes:
|
||||
- stub-data:/data
|
||||
environment:
|
||||
STUB_ADMIN_PORT: "8080"
|
||||
STUB_DATA_DIR: "/data"
|
||||
STUB_LOG_LEVEL: "debug"
|
||||
LLM_PROVIDER: "anthropic"
|
||||
LLM_API_KEY: "${LLM_API_KEY}"
|
||||
LLM_MODEL: "claude-sonnet-4-6"
|
||||
|
||||
volumes:
|
||||
stub-data:
|
||||
```
|
||||
|
||||
**Configuration (environment variables):**
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `STUB_ADMIN_PORT` | `8080` | Admin API and Web UI port |
|
||||
| `STUB_DATA_DIR` | `./data` | SQLite database directory |
|
||||
| `STUB_LOG_LEVEL` | `info` | Log level (debug, info, warn, error) |
|
||||
| `STUB_PORT_RANGE_START` | `4545` | Start of port range for stub listeners |
|
||||
| `STUB_PORT_RANGE_END` | `4600` | End of port range for stub listeners |
|
||||
| `LLM_PROVIDER` | (none) | LLM provider: `anthropic` or `openai` |
|
||||
| `LLM_API_KEY` | (none) | LLM API key |
|
||||
| `LLM_MODEL` | `claude-sonnet-4-6` | Model to use for stub generation |
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- **Single binary** — no runtime dependencies beyond the OS; trivial to deploy.
|
||||
- **Embedded Web UI** — no separate frontend build/deploy pipeline; Templ + htmx keeps it all in Go.
|
||||
- **SQLite** — zero operational overhead, no external database to manage, easy backup (copy a file).
|
||||
- **LLM integration** — significantly lowers the barrier for creating complex stubs, especially for QA engineers unfamiliar with JSONPath/regex predicates.
|
||||
- **Mountebank-compatible concepts** — teams familiar with Mountebank can transfer their mental model (imposters, stubs, predicates).
|
||||
- **Port-per-imposter isolation** — each mock service gets its own port, matching how real services are addressed.
|
||||
|
||||
### Negative
|
||||
|
||||
- **Port range management** — dynamic port allocation requires firewall/Docker port-range configuration.
|
||||
- **SQLite single-writer** — under very heavy concurrent management API load, SQLite's single-writer lock could be a bottleneck. Acceptable for a testing/dev tool.
|
||||
- **LLM dependency** — the generation feature requires external API access and incurs cost. It is optional — the service works fully without it.
|
||||
- **No multi-protocol support initially** — only HTTP/HTTPS. TCP/SMTP stubs (which Mountebank supports) are deferred to a future iteration.
|
||||
|
||||
### Risks
|
||||
|
||||
- **Templ maturity** — Templ is relatively new. Mitigation: the UI is simple enough that switching to `html/template` is feasible with moderate effort.
|
||||
- **JSONPath/XPath complexity** — Go's ecosystem for JSONPath and XPath is less mature than Node.js. Mitigation: use well-maintained libraries ([github.com/PaesslerAG/jsonpath](https://github.com/PaesslerAG/jsonpath), [github.com/antchfx/xmlquery](https://github.com/antchfx/xmlquery)) and test thoroughly.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Mountebank](http://www.mbtest.org/) — the inspiration
|
||||
- [chi router](https://github.com/go-chi/chi)
|
||||
- [Templ](https://templ.guide/) — Go HTML templating
|
||||
- [htmx](https://htmx.org/) — HTML-driven interactivity
|
||||
- [modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite) — pure Go SQLite
|
||||
Loading…
Reference in New Issue