Both read release-0.0.1 — the branch every commit of this service is
actually on — after the merge rather than on a pull request; the per-PR gates
are in ci.yml. The security badge links to the
alert list, which is public because this repository is.
A Go service exposing synchronous POST /publish and POST /discover that
ingest Beckn v2.0.0 catalogs into PostgreSQL and
serve geo + lexical discovery. One discover over ten thousand resources answers
in 19-22 ms; sixteen concurrent ones do not, and the plan's A16 records the
measurement and what it means.
- Architecture:
controller → service → repository. Two ports live insrc/domain/:CatalogRepository(write, driven by publish) andSearchRepository(read, driven by discover). Neither capability package imports a driver, so the backend is swappable. - Stack: Go 1.25 ·
net/http.ServeMux· pgx/v5 + sqlc · PostgreSQL 16 + pgvector 0.8 · uber/h3-go v4 · kin-openapi · zap · testify · testcontainers-go.
docs/ is the way in. Four short documents — a
quickstart, how publish and
discover work, the
telemetry and the registry — plus the
API artefacts and the sixteen
ADRs.
The specification is
docs/design/implementation-plan.md, and
it is binding rather than advisory. Read it before changing anything here; where
it and one of the four documents above disagree, it wins.
Go 1.25 or later, and Docker for the local database and the integration
suites. Nothing else needs installing: every build tool is pinned in
tools/go.mod and built into bin/ on demand.
make up # PostgreSQL 16 + pgvector 0.8, waited on until healthy
make migrate # apply migrations to DATABASE_URL
make build # compile every package and link bin/discovery-service
make test # unit and integration suitesmake on its own lists every target.
To try the service rather than change it, run the whole stack in Docker:
make run # PostgreSQL + the service on :8080, migrations applied on boot
make logs # follow the service's output
make down # stop everything and discard the volumes
curl localhost:8080/readyzmake up deliberately stays dependencies-only. The integration suites reach
PostgreSQL through testcontainers and never through Compose, and development
runs the binary from the host — so the service container lives behind the app
Compose profile, which make run selects.
That stack applies its own migrations (they are compiled into the binary) and
reads the Beckn specification from tests/testdata/beckn-v2.0.0.yaml, mounted
at the cache path. The boot logs one warning about the registry fetch it did
not do, which is why it works with no network. That file is byte-identical to
the core-v2.0.0-lts tag of beckn/protocol-specifications-v2; set
VALIDATION_SPEC_URL to its raw URL to exercise the fetch path instead.
With the stack up, there is a worked catalog and the requests that find it:
make verify # publish the sample catalog, assert text, spatial and filter retrieval
make newman # the same checks through the Postman collection
make audit # recompute the expected answers and check the service agreesThe first two assert the exact set of resource ids each request returns, and
the cases are built to disagree with each other. make audit answers the
question those two cannot: their id sets were written by watching this service
run, so they pin what it did rather than what is correct. The audit recomputes
each expectation from the published catalog with its own geometry and text
matching, validates every response against beckn.yaml, and checks each
returned resource is byte-identical to what was published. See
examples/ for what each case pins and why.
cmd/discovery-service/ entrypoint
src/publish/ SYSTEM 1 — catalog ingest
src/discover/ SYSTEM 2 — search
src/domain/ the contract between them; stdlib + uuid only
src/beckn/ Beckn v2.0.0 wire types
src/indexing/ H3 geospatial covers, embeddings
src/storage/ postgres, memory, and the conformance suite both pass
src/platform/ config, logging, errors, crypto, validation, middleware,
httpx, jsonpath, registry
src/app/ composition root
config/ migrations/ schemas/ tests/ docs/
tools/ separate module pinning the build toolchain
Four layers, lowest precedence first: envDefault struct tags →
config/common.yaml → config/instance.yaml → process environment.
Environment sits on top because secrets arrive from a secret store and must
beat a file.
Copy config/instance.yaml.example to config/instance.yaml for deployment
overrides; that file is gitignored. Secrets — DATABASE_URL above all —
belong in neither YAML file.
The Beckn specification is fetched at boot from VALIDATION_SPEC_URL and
cached under .cache/beckn/. It defaults to the core-v2.0.0-lts tag of
beckn/protocol-specifications-v2 — a tag rather than a branch, so an upstream
merge cannot change the validator under a running deployment. It is not baked
into the image; air-gapped deploys mount a cache file at
VALIDATION_SPEC_CACHE_PATH.
Contribution policy and a security disclosure process have not been decided
yet, so this repository deliberately carries no CONTRIBUTING.md or
SECURITY.md — a placeholder would publish a promise nobody has made. Until
they exist, raise an issue.
Undecided. This repository carries no LICENSE, for the same reason it carries
no CONTRIBUTING.md — the decision has not been made, and a licence file is the
one placeholder that cannot be corrected later without the consent of everyone
who relied on it. Until one is chosen, no licence is granted.