A minimalist production-ready backend MVP split into four independent apps:
- Landing pages (
cmd/landing-pages) - REST API (
cmd/rest-api) - MCP server (
cmd/mcp-server) - Channel connections service (
cmd/channel-connections)
- Go 1.24.0+
- PostgreSQL
- MinIO (S3-compatible payload storage)
- GORM
- MCP:
github.com/mark3labs/mcp-go - Tests:
github.com/stretchr/testify
make docker-upServices:
- Landing pages:
http://localhost:8080 - REST API:
http://localhost:8081 - MCP endpoint (Streamable HTTP):
http://localhost:8090/mcp - MCP compatibility alias:
http://localhost:8090/sse - MCP discovery endpoint:
http://localhost:8090/.well-known/mcp-server - MCP health endpoint:
http://localhost:8090/healthz - Channel connections (SSE + internal MCP read backend):
http://localhost:8070 - Postgres:
localhost:5432 - MinIO API:
http://localhost:9000 - MinIO Console:
http://localhost:9001
Channel URLs returned by create channel operations use CHANNEL_HOSTNAME (not REST/MCP bind address), e.g. https://hostname.io/channel/v1/public/{channel_id}.
Stop:
make docker-downDeploy using docker context pdfturn:
TOKEN_SECRET_SALT=replace-me make deploy-prodTeardown:
make deploy-prod-downNotes:
- Production compose file:
docker-compose.prod.yml - Caddy config:
deploy/caddy/Caddyfile - Caddy routes:
api.chantra.io->rest-apimcp.chantra.io->mcp-serverchantra.io->landing-pages/channel/*onchantra.io->channel-connections
- Start Postgres and MinIO (for example, with
docker compose up postgres minio minio-init -d) - Copy
.env.exampleto.envand fill in the secrets - Apply migrations:
make migrate-up- Start services:
make run-rest
make run-mcp
make run-channel-connections
make run-landingmake runmake run-landingmake run-allmake run-restmake run-mcpmake run-channel-connectionsmake buildmake testmake test-unitmake test-integrationmake lintmake migrate-upmake migrate-downmake docker-upmake docker-downgolangci-lint rungovulncheck ./...
Health:
GET /healthz
Channels:
POST /v1/channelsGET /v1/channels/{channelID}DELETE /v1/channels/{channelID}POST /v1/channels/{channelID}/publish
Read grants:
POST /v1/channels/{channelID}/read-grantsPOST /v1/channels/{channelID}/read-grants/{grantID}/revoke
Read messages:
- REST API does not expose message-read endpoints.
- Public/direct reads use channel URL SSE through
https://.../channel/v1/{public|private}/{channel_id}. - The channel URL path itself is the SSE endpoint when resolved through HTTPS, e.g.
https://chantra.io/channel/v1/public/{channel_id}. - channel-connections serves this as
GET /v1/{public|private}/{channelID}(via edge/channel/*), supportsLast-Event-ID. - Private-channel read auth headers:
X-Read-TokenorAuthorization: Bearer <token>. - MCP reads use
read_channel_messages, which calls channel-connections internal endpointGET /internal/v1/channels/{channelID}/messages.
Notes:
- No users, no email, and no auth/registration endpoints.
POST /v1/channelsis public and returns one-timepublish_token(read_tokenalso returned for private channels).- Management/publish operations require header
X-Publish-Token. - Read and publish tokens are different.
- Global rate limiting returns HTTP
429. - Channel lifecycle: channels with no published message activity for 30 days are deleted with related data.
create_channeldelete_channelpublish_messagecreate_read_grantrevoke_read_grantget_channelread_channel_messages(uses channel-connections service)
- Only token hashes (
publish key,read grant) are stored in DB. - Raw tokens are returned only at creation time.
- Message cursoring uses sortable message IDs (UUIDv7), not per-channel sequence IDs.
- Messages expired by TTL are not returned when reading
- Binary
payloadis stored in MinIO, and onlypayload_object_keyis saved in the DB - In the API, payload is returned as
payload - Maximum message TTL:
48h(172800seconds) - Lifecycle-based object deletion is enabled for the MinIO bucket after 2 days
- Production services run with
AUTO_MIGRATE=false. - Run migrations explicitly before deploying services.
- Down migrations can be destructive and should be run only in controlled rollback procedures.
- See SECURITY.md for vulnerability reporting.
- MIT, see LICENSE.