> ## Documentation Index
> Fetch the complete documentation index at: https://isthmus.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> Hexagonal architecture, directory structure, and how to extend Isthmus.

Isthmus follows hexagonal architecture (ports & adapters). The core business logic is decoupled from infrastructure, making it straightforward to add new database backends or change the transport layer.

## Directory structure

```
cmd/isthmus/               → Binary entrypoint (single binary, stdio + HTTP transports)
internal/
  core/
    port/                  → Interfaces: SchemaExplorer, QueryExecutor, QueryAuditor
    domain/                → SQL validation (pg_query AST), cardinality, column masking (MaskType)
    service/               → Application services: QueryService
  adapter/
    mcp/                   → MCP server factory + tool definitions (discover, describe_table, query)
    postgres/              → PostgreSQL implementation of ports (pgxpool, information_schema, pg_stats)
  audit/                   → File-based audit logging (NDJSON)
  config/                  → Environment variable + CLI flag loading
  policy/                  → Policy engine: YAML loading, context enrichment, column masking
  telemetry/               → OpenTelemetry: OTLP gRPC providers, metric instruments
```

Everything is under `internal/` — Go's convention enforces module privacy. There is no `pkg/` directory.

## Ports (interfaces)

The core defines three port interfaces that adapters implement:

```go theme={null}
// Schema discovery and table analysis
type SchemaExplorer interface {
    ListSchemas(ctx context.Context) ([]SchemaInfo, error)
    ListTables(ctx context.Context) ([]TableInfo, error)
    DescribeTable(ctx context.Context, schema, tableName string) (*TableDetail, error)
    Discover(ctx context.Context) (*DiscoveryResult, error)
}

// SQL execution
type QueryExecutor interface {
    Execute(ctx context.Context, sql string) ([]map[string]any, error)
}

// Audit trail
type QueryAuditor interface {
    Record(ctx context.Context, entry AuditEntry)
    Close() error
}
```

## Data flow

1. The MCP client sends a tool call (e.g. `describe_table`) over stdio or HTTP
2. The MCP adapter routes it to the appropriate service
3. The service calls the port interface (e.g. `SchemaExplorer.DescribeTable`)
4. The Postgres adapter executes SQL against the database
5. The policy engine enriches the response and applies column masks (if configured)
6. The result is serialized to JSON and returned to the client
7. If OpenTelemetry is enabled, spans and metrics are recorded at each step

## Dependency injection

All dependencies are wired at startup in `cmd/isthmus/main.go`. There is no global state, service locator, or runtime reflection:

```go theme={null}
// Adapters
pool     := postgres.NewPool(ctx, cfg.DatabaseURL)
explorer := postgres.NewExplorer(pool, cfg.Schemas)
executor := postgres.NewExecutor(pool, cfg.ReadOnly, cfg.MaxRows, cfg.QueryTimeout)

// Optional decorators
if cfg.PolicyFile != "" {
    pol, _ := policy.LoadFromFile(cfg.PolicyFile)
    masks  := policy.MaskSpec(pol.Context)
    explorer = policy.NewPolicyExplorer(explorer, pol, masks)
}

// Services
querySvc := service.NewQueryService(validator, executor, auditor, logger)

// Server
mcpServer := mcp.NewServer(ver, explorer, querySvc, logger)
```

## Adding a new database adapter

To add support for a new database (e.g. MySQL):

1. Create `internal/adapter/mysql/`
2. Implement `port.SchemaExplorer` and `port.QueryExecutor`
3. Wire the new adapter in `cmd/isthmus/main.go` based on the connection string scheme
4. Add integration tests using testcontainers

The MCP layer, services, domain validation, and policy engine all work unchanged — they only depend on port interfaces.

## Testing

Isthmus uses **no mocks**. Integration tests use [testcontainers-go](https://golang.testcontainers.org/) to spin up real PostgreSQL containers:

```bash theme={null}
go test -race -count=1 ./...                  # All tests (needs Docker)
go test -short -race -count=1 ./...           # Unit tests only
go test ./internal/core/domain/...            # Domain tests only
go test ./internal/adapter/postgres/...       # Postgres adapter tests
```

Domain tests (SQL validation, cardinality classification) run without Docker. Adapter and E2E tests require Docker for testcontainers.
