BlockGo is a minimal, open-source, educational blockchain project in Go.
It is designed to teach core blockchain and Go concepts through a small but real implementation: UTXO state, signed transactions, signed blocks, fixed-validator Proof of Authority, local persistence, basic peer sync, a tiny HTTP API, and a runnable multi-node demo.
- keep the codebase small, readable, and teachable
- prefer explicit design over framework-heavy abstraction
- demonstrate realistic node-to-node behavior without hiding the moving parts
- keep dependencies minimal and contributor workflow simple
BlockGo currently includes:
- core blockchain data structures
- transaction hashing and signing
- UTXO validation and state transitions
- genesis creation and validation
- bbolt-backed persistence
- Proof of Authority consensus with a fixed validator set
- minimal TCP peer-to-peer sync
- node runtime with block production
- CLI utilities
- optional HTTP API
- Docker Compose local multi-node demo
- CI and release automation
The current project intentionally does not include:
- Proof of Work
- full Proof of Stake
- smart contracts or a VM
- dynamic peer discovery
- dynamic validator set changes
- advanced fork choice or finality gadgets
- hostile-network hardening
- production-scale performance tuning
- Ledger model: UTXO
- Consensus: Proof of Authority with a fixed validator set and deterministic proposer selection
- P2P: small custom TCP protocol with static peers
- Storage: bbolt
- Crypto: ed25519 for signatures and sha256 for hashing
- Approach: stdlib-first and minimal dependencies
For the exact behavior and contracts, see docs/spec.md.
- Go
1.24.10 - Make
- Docker and Docker Compose for the local multi-node demo
make buildmake testmake cimake run-cli./bin/blockgo-node -versionAfter building, the CLI binary is available at ./bin/blockgo.
blockgo versionblockgo generate-keyblockgo gen-keyblockgo address -pubkey <hex>blockgo create-tx ...blockgo gen-localnet [-mode docker|local] [-nodes n] -out <dir>
./bin/blockgo generate-keyExample output:
{
"public_key_hex": "<hex>",
"private_key_hex": "<hex>",
"address": "<address-hex>"
}./bin/blockgo address -pubkey <public-key-hex>./bin/blockgo create-tx -in-prev-tx <tx-id-hex> -in-index 0 -in-pubkey <public-key-hex> -in-privkey <private-key-hex> -out-address <recipient-address-hex> -out-value 100Example with a change output:
./bin/blockgo create-tx -in-prev-tx <tx-id-hex> -in-index 0 -in-pubkey <public-key-hex> -in-privkey <private-key-hex> -out-address <recipient-address-hex> -out-value 100 -change-address <change-address-hex> -change-value 900The quickest runnable local path is:
make run-nodeThat target generates a dedicated single-node local development config in configs/run-node and starts node1 with:
- loopback listen addresses such as
127.0.0.1:7001 - a local genesis file at
configs/run-node/genesis.json - an isolated local data directory under
data/run-node/ - generated validator keys for local development only
You can generate the same local config explicitly with:
go run ./cmd/blockgo gen-localnet -mode local -nodes 1 -out ./configs/run-node
./bin/blockgo-node -config ./configs/run-node/node1.jsonTracked example configs are still available as templates:
configs/genesis.example.jsonconfigs/node.example.json
These tracked example files intentionally contain placeholder keys and addresses. Fill them in before using them directly.
go run ./cmd/blockgo gen-localnet -mode docker -nodes 3 -out ./configs/localThis creates:
configs/local/genesis.jsonconfigs/local/node1.jsonconfigs/local/node2.jsonconfigs/local/node3.json
These generated files contain local demo private keys and should not be committed. Docker mode is the default and produces Compose-oriented addresses such as node2:7002 and /app/configs/local/genesis.json.
docker compose up --buildcurl http://localhost:8001/healthz
curl http://localhost:8002/healthz
curl http://localhost:8003/healthzcurl http://localhost:8001/v1/chain/headcurl http://localhost:8001/v1/mempool- all nodes share the same generated
genesis.json - validator keys and node configs are generated automatically
- generated files are intended for local development and demo use only
- the local demo uses static peers and a fixed validator set
The node exposes a small optional HTTP API.
GET /healthzGET /v1/chain/headGET /v1/mempoolPOST /v1/transactions
Example payload:
{
"inputs": [
{
"prev_tx_id": "<tx-id-hex>",
"output_index": 0,
"public_key_hex": "<public-key-hex>",
"signature_hex": "<signature-hex>"
}
],
"outputs": [
{
"value": 100,
"address": "<recipient-address-hex>"
}
]
}Example call:
curl -X POST http://localhost:8001/v1/transactions -H "Content-Type: application/json" --data @tx.jsonSee docs/spec.md for the config and API contract details.
cmd/ Executables
internal/ Private implementation packages
configs/ Example config files
docs/ Core project documentation
scripts/ Helper scripts
test/ Integration test area
bin/ Local build output
.github/ OSS templates and CI
The project documentation is intentionally centered on four core docs:
README.md: entry point, workflow, contribution guidance, and community expectationsdocs/spec.md: behavior, data contracts, config, and API surfacedocs/architecture.md: package boundaries, runtime flow, and system structuredocs/roadmap.md: current status, milestones, release direction, and deferred work
This documentation split is a deliberate refactor decision to keep the project easier to scan, easier to maintain, and less prone to duplicated or stale guidance. Durable material that used to live in standalone config, release, contributing, and conduct docs now lives in these four documents.
Please keep changes:
- simple, explicit, and easy to review
- aligned with the educational scope of the project
- focused on correctness, readability, and maintainability
- accompanied by doc updates when behavior changes
- accompanied by tests when validation, state, storage, sync, or API behavior changes
Prefer:
- small, focused packages
- standard library solutions unless a dependency clearly reduces complexity
- straightforward control flow over abstraction-heavy patterns
- clear error messages and readable names
- deterministic blockchain rules and explicit validation
Discuss first before making major changes to:
- architecture
- consensus rules
- storage model
- networking protocol shape
- dependency strategy
- large API surface changes
Keep pull requests small when practical. A good pull request explains:
- what changed
- why the change was needed
- how it was validated
- any follow-up work or notable limitations
Use conventional commits where practical, for example:
feat: ...fix: ...docs: ...test: ...chore: ...
Project spaces include issues, pull requests, discussions, and related collaboration channels.
Please:
- be respectful, constructive, and inclusive
- give actionable technical feedback
- focus on the code and ideas, not the person
- avoid harassment, personal attacks, discrimination, or deliberate disruption
Maintainers may edit, remove, or reject comments, issues, pull requests, or contributions that do not meet these expectations.
- do not commit private keys, generated demo secrets, local data directories, or machine-specific credentials
- generated local demo configs are for development only and should not be committed
- if you notice a security issue, share a minimal report and avoid posting sensitive details publicly
By contributing to BlockGo, you agree that your contributions are licensed under the repository's MIT license.
Common commands:
make fmt
make vet
make test
make build
make ciUseful local commands:
make run-cli
make run-node
go run ./cmd/blockgo gen-localnet -mode docker -nodes 3 -out ./configs/local
docker compose up --buildMIT