ἄρχων — the officeholder; the one who bears the office.
Who you provably are — as bytes anyone can re-check.
Ed25519 identity with one spelling. A key is ed25519:<64 hex> — the same 32 bytes, the
same text, in Rust, Go and TypeScript, checked against one oracle on every push. The PEM
codecs are RFC 5280 SPKI and RFC 5958 PKCS-8, nothing invented. Signing is
domain-separated, so a signature made for one context cannot count in another.
It answers exactly two questions — are these bytes that key? and is this signature that key's? — and deliberately answers nothing else. What a key is allowed to do, and where a key is kept, are somebody else's questions. See Scope.
$ archon keygen --out seed.hex --pub-out key.txt --pub-format text
$ cat key.txt
ed25519:d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
$ echo -n "hello" | archon sign --seed "$(cat seed.hex)" --domain example.v1 > sig.hex
$ echo -n "hello" | archon verify --pubkey "$(cat key.txt)" --sig "$(cat sig.hex)" --domain example.v1
validverify prints valid or invalid and exits 0 or 1 — a verdict on stdout, not a usage
error, so a failed check is scriptable.
The same three operations, as a library:
import (
"github.com/Bitspark/archon/core/go/crypto"
"github.com/Bitspark/archon/core/go/keytext"
)
pub := crypto.PublicKeyFromSeed(seed) // 32 bytes -> 32 bytes
text := keytext.EncodeKey(pub) // -> "ed25519:d75a98…"
sig := crypto.SignInDomain("example.v1", seed, msg)let public = archon_core::public_key_from_seed(&seed);
let text = archon_core::encode_key(&public);
let sig = archon_core::sign_in_domain("example.v1", &seed, msg);import { getPublicKey, encodeKey, signInDomain } from "@bitspark/archon";
const pub = getPublicKey(seed);
const text = encodeKey(pub);
const sig = signInDomain("example.v1", seed, msg);Rust, Go and TypeScript are not three ports that drifted. They are held to one hand-authored
oracle in vectors/, and the harness recomputes every case rather than
trusting the recorded answer:
$ node conformance/check.mjs
all cores agree: 180 case-checks, 3 core(s)That runs on every push. If the cores ever disagree about whether something is a valid key, a valid signature or a valid key text, that disagreement is the bug — a consumer's two ends may not be written in the same language.
vectors/ is a published oracle: an independent implementation can check itself against it
without using any of this code.
| what it adds | |
|---|---|
| core | key bytes, the canonical key text, SPKI/PKCS-8, domain-separated sign and verify |
| sdk | signed envelopes, proof of possession, and the login scheme's audience binding |
| cli | the archon command — keygen, key, sign, verify, login, version |
| server | the login endpoint: proof-of-possession sign-in and key-to-key delegation |
# Go — resolves straight from this repository
go get github.com/Bitspark/archon/core/go # also /sdk/go, /cli/go, /server/go
# Rust — the package name carries the vendor prefix; what you `use` does not
cargo add bitspark-archon-core # use archon_core::…
# also -sdk, -cli, -server
# TypeScript
npm install @bitspark/archon # also -sdk, -cli, -server
# Python — import archon_core, archon_sdk
pip install bitspark-archon-core # also bitspark-archon-sdk
# Java — Maven Central; also artifactId archon-sdk
# <dependency><groupId>dev.bitspark</groupId><artifactId>archon-core</artifactId>
# <version>0.8.1</version></dependency>The Go modules resolve directly from this repository and need nothing else; the others are
on their public registries. Python and Java publish the floor and an sdk (possession and
envelope, not the login scheme) as bitspark-archon-sdk and dev.bitspark:archon-sdk,
first released in 0.8.1. Which language carries which
tier, and how each claim is proven, is in docs/languages.md.
The command is the same in all three languages — install it from whichever you already have:
go install github.com/Bitspark/archon/cli/go/cmd/archon@latest
npm install -g @bitspark/archon-cli
cargo install bitspark-archon-cli # installs the binary `archon`On crates.io the packages carry a bitspark- prefix, because crates.io has a single flat
namespace with no scopes and the short archon-* names are not all available. It is a
registry name only: the library target keeps its own name, so you write
use archon_core::…, and the installed binary is archon.
Runtime dependencies, in full: ed25519-dalek (Rust) · the standard library's
crypto/ed25519 plus filippo.io/edwards25519 for point validation only (Go — the
published form of the implementation the standard library is maintained from; see ADR 0008
§5 for why a blocklist was not enough) · @noble/curves and @noble/hashes (TypeScript,
because noble v3 unbundles SHA-512 and makes you supply it) · PyCryptodome (Python, the one
mainstream route to Ed25519ph with a context). Nothing else, in any language, at any
version. Each core binds its language's Ed25519 and archon writes only the encodings and
the checks on top; the curve arithmetic is not reimplemented here. What every core
accepts is written down once, in ADR 0008,
and checked ahead of the library rather than inherited from it.
archon owns what can be stated without a law — in RFC 8032, RFC 5280 and RFC 5958 vocabulary alone: a seed, a public key, a signature, an opaque message, a key text, a PEM.
It deliberately does not own:
- Authority. Whether a key may do a thing is not archon's question. The login server
hands the authority payload to the consumer through
AdmitAuthorityand never reads it. - Custody. Storing a key, encrypting it at rest, rotating it, running a root ceremony,
retiring an epoch — none of it.
archon keywrites a file you name and reads a file you name; it manages nothing and is not a keychain. - Succession. There is no re-key path. A key dies with itself; loss or compromise means generating a new one.
ADR 0001 states the criterion, and works an example in which archon argued to take something and was — correctly — refused. That example is the useful part: the value of one canonical spelling is real but it is not universal, and a project that already has a canonical byte-to-text spelling would be adding a second by adopting this one.
- Architecture decisions — why the boundaries are where they are
- The login scheme — proof of possession, audience binding, delegation
- The key store — what
archon keydoes and does not do - Conformance and the oracle — how agreement is established
- CONTRIBUTING.md · SECURITY.md · CODE_OF_CONDUCT.md