base had its own key derivations and no dependency on hanzoai/cek, the
primitive that is meant to be the only one. Two HKDFs in the platform
plugin, two HMACs beside them for S3, and a two-level HMAC hierarchy in
the vault. None agreed with cek, and the comments describing them had
drifted off the code: org_db and platform.go both said HMAC where the
code ran HKDF, and the vault drew a KEK hierarchy in its package doc.
All of them are now cek.DeriveKey(master, namespace, subsystem):
org database org/{org} "org"
user database org/{org}/{user} "user"
org objects org/{org} "s3"
user objects org/{org}/{user} "s3"
vault shard org/{org}/{user} "vault"
shared vault org/{org}/{vault} "vault-shared"
A per-org and a per-user key are one derivation over two namespaces,
which is cek's argument: the key is bound to the name that renders the
path. namespace.Key refuses a user namespace, but that is about LAYOUT
and DeriveKey does not use it — a person's data is named org/{org}/{user}
here, which is what namespace.Of already builds, and it is what keeps
acme/alice and globex/alice on different keys. The vault reaches the same
namespace through OrgProject, because its ids are DIDs and a colon is not
legal in a segment; Sanitize is injective, so two DIDs cannot fold onto
one vault.
The vault's org KEK is gone. It was there to be rotatable without
re-encrypting user data, nothing ever rotated it, and the org is in the
namespace now — the property it was actually providing.
store/keyring.go keeps its own HKDF, and now says why. It WRAPS a random
age identity rather than deriving a key, so Rotate re-wraps one sidecar
instead of rewriting every byte of ciphertext, and its secret is a
per-org KMS KEK rather than one master. cek has no wrap by design. A
different shape, left alone rather than bent into one.
Tests now compare each derivation against cek.DeriveKey directly. A
comment claiming a derivation cannot fail when the derivation drifts,
which is exactly how the one deleted last time survived.
A master key of the wrong length is now an error instead of silently no
key, and the resolver's encKey, computed and never read, is gone.
deps: cek v0.2.3 and namespace v1.2.0, plus tasks v1.52.4 -> v1.52.9,
which is what moves hanzoai/sqlite to v0.5.0 — tasks below v1.52.9 still
calls the wrapped-DEK API that v0.5.0 removed.
Co-authored-by: Hanzo Dev <[email protected]>
core.App.Logger() now returns logger.Logger (the new shim interface)
instead of *slog.Logger, so every consumer talks to the same
backend-agnostic surface and stops pinning itself to log/slog. The raw
*slog.Logger is still reachable as App.SlogLogger() for the in-app
BatchHandler reload hook and the two tests that type-assert the
underlying handler — that's the only sanctioned escape hatch.
Plugins and network code that previously held *slog.Logger fields move
to luxlog.Logger (kms, vault, zap-transport, base-network/transport_zap,
network/archive_writer, tools/tasks/client). luxfi/zap's Logger field
was a pure transit dependency; we drop it from base call sites since
zaplib has its own default.
Remaining "log/slog" imports — 11 files — are confined to the
adapter/handler bridge (tools/logger and core/log_printer/base/app) and
to two tests that exercise the slog.Handler contract directly. Those
are the irreducible boundary where slog is the language of the
interface.
ZAP binary protocol duplex RPC standardizes on :9999 across every
service. Lux validator staking/RPC ports (9630/9631 mainnet, 9650/9651
devnet) are unchanged — those are luxd network protocol ports, not ZAP.
One ZAP port everywhere (was a mix of 9651/9652/9653/9633/9900).