For most blockchain clients it is essential to keep track of transaction nonces that protect against duplicate transactions and replay attacks.
You can read more about nonces here. A non-partitioned client application — a mobile wallet, MetaMask — will easily do this for you. Tracking nonces gets trickier once your application needs to run in a distributed, split-brain-tolerant fashion.
dinonce is a nonce ticketing service that lets multiple tx executors share a single account's nonce sequence while avoiding:
- double spending,
- and gaps in the nonce sequence that would fill a network's tx pool.
dinonce supports ticketing for multiple nonce sequences in parallel. An identity on a given blockchain has its own sequence of nonces; dinonce calls that sequence a lineage.
For each lineage a client gets a leased nonce ticket for a transaction,
identified by an externalId that uniquely names the transaction in the
calling system. If the operation succeeds, dinonce holds a lease on the
newly-associated nonce.
Because most tx executors operate with at-least-once semantics, the transaction can either:
- complete (be mined on-chain), in which case the client closes the ticket so the nonce can never be re-leased, or
- fail for non-transient reasons, in which case the client releases the ticket so the nonce is re-assigned to the next lease request, avoiding a gap in the sequence.
For EOAs the mapping is one lineage per (chain, sender). For ERC-4337
account-abstraction wallets the mapping is one lineage per
(chain, sender, nonceKey) because UserOperations support 2D nonces. See
ADR-0001 for the full
discussion.
dinonce is built contract-first with OpenAPI 3.0. The spec lives at
api/api.yaml and is the source of truth for the generated
server stubs in internal/api/generated/.
Generate a client library in any language with the openapi-generator.
| Port | Path | Purpose |
|---|---|---|
| 5010 | /lineages… |
OpenAPI surface (see api/api.yaml) |
| 5010 | /metrics |
Prometheus metrics |
| 5010 | /version |
Build metadata (version, commit, build date) |
| 5001 | /livez |
Liveness probe — process is running (no dependency check) |
| 5001 | /readyz |
Readiness probe — registered dependencies are healthy |
dinonce reads config.yaml from /opt/dinonce/config/, $HOME/.dinonce/config/,
or ./.config/. Every key can also be supplied as an environment variable
prefixed with DINONCE_ and dots replaced with underscores, e.g.:
DINONCE_BACKENDCONFIG_HOST=db.internal
DINONCE_BACKENDCONFIG_PORT=5432
DINONCE_BACKENDCONFIG_USER=dinonce
DINONCE_BACKENDCONFIG_PASSWORD=... # mount via Kubernetes Secret
DINONCE_LOGGER_LEVEL=infoConnection-pool knobs:
| Key | Default | Purpose |
|---|---|---|
backendConfig.maxOpenConns |
25 | Max concurrent DB connections |
backendConfig.maxIdleConns |
5 | Max idle connections kept around |
backendConfig.connMaxLifetime |
30m | Recycle connections after this age |
Images are published to GHCR at
ghcr.io/matelang/dinonce.
They are multi-arch (linux/amd64 and linux/arm64), distroless-based, and
signed with cosign (keyless OIDC). A CycloneDX SBOM is attached to every
release archive.
To verify an image:
cosign verify ghcr.io/matelang/dinonce:vX.Y.Z \
--certificate-identity-regexp='^https://github.com/matelang/dinonce/' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com'A Helm chart lives under deployments/helm. The
operator is responsible for creating the dinonce-config ConfigMap (or
Secret) containing a config.yaml, structured like
.config/config.yaml.
A Terraform module under
deployments/terraform/modules/helm-aws-rds-psql
provisions an AWS RDS Aurora PostgreSQL database, creates a namespace in
Kubernetes, and deploys the Helm chart; see the
example.
dinonce is designed to support multiple storage backends provided they respect the lineage/ticket semantics. The current backend is PostgreSQL. Contributions implementing other backends are welcome — see CONTRIBUTING.md.
make oapi # regenerate OpenAPI stubs after editing api/api.yaml
make build # build the binary with embedded version
make test # unit tests (-race, fast)
make test-integration # spin up Postgres on :5433, run integration tests
make lint vuln sec # golangci-lint, govulncheck, gosec (all via `go tool`)
make cover # full coverage including integrationSee CONTRIBUTING.md for the contribution workflow and SECURITY.md for the vulnerability-reporting process.
dinonce was originally developed at welthee; this
repository is an actively-maintained fork. The v2 module path
(github.com/welthee/dinonce/v2) is frozen; new work happens against the v3
module path (github.com/matelang/dinonce/v3).
