@bandeira-tech/b3nd-canon
v0.15.1
Published
B3nd Canon — protocol-building toolkit: msg, auth (hash + encrypt re-exported from b3nd-core)
Readme
B3nd Canon
Protocol-building toolkit for B3nd. Message envelopes, content addressing, access control, and encryption -- the pieces a protocol designer composes on top of the core framework.
Depends on @bandeira-tech/b3nd-core for types and encoding.
Message Layer
The message primitive is [uri, payload]. When the payload follows the
MessageData convention it carries { auth, inputs, outputs } -- a signed
envelope that the rig decomposes into individual writes.
import {
message,
messageDataHandler,
messageDataProgram,
} from "@bandeira-tech/b3nd-canon/msg";
import {
connection,
DataStoreClient,
Identity,
MemoryStore,
Rig,
} from "@bandeira-tech/b3nd-core";
const client = new DataStoreClient(new MemoryStore());
const rig = new Rig({
routes: {
receive: [connection(client, ["*"])],
read: [connection(client, ["*"])],
},
programs: { "hash://sha256": messageDataProgram },
handlers: { "msgdata:valid": messageDataHandler },
});
const id = await Identity.generate();
const auth = [
await id.sign({ inputs: [], outputs: [["mutable://open/x", { v: 1 }]] }),
];
const envelope = await message({
auth,
inputs: [],
outputs: [["mutable://open/x", { v: 1 }]],
});
// envelope = ["hash://sha256/{hex}", { auth, inputs, outputs }]
await rig.send([envelope]);
// The handler decomposes the envelope: persists the envelope at hash://,
// writes each output to its destination URI, and nullifies inputs.Content Addressing
Hash-based URIs using hash://sha256/{hex}. JSON payloads canonicalized per RFC
8785 before hashing.
import {
computeSha256,
generateHashUri,
verifyHashContent,
} from "@bandeira-tech/b3nd-canon/hash";
const hash = await computeSha256({ hello: "world" });
const uri = generateHashUri(hash);
// "hash://sha256/93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588"
const result = await verifyHashContent(uri, { hello: "world" });
// { valid: true, algorithm: "sha256", digest: "93a2..." }Hash Validator
Write-once enforcement for content-addressed storage:
import { hashValidator } from "@bandeira-tech/b3nd-canon/hash";
const rig = new Rig({
routes: { ... },
programs: { "hash://sha256": hashValidator(readFn) },
});Access Control
Signature-based access control that composes with the rig as programs.
import {
authValidation,
createCombinedAccess,
createPubkeyBasedAccess,
} from "@bandeira-tech/b3nd-canon/auth";
// Pubkey-based: the host names the domain (the program is
// protocol://host); the owner pubkey is the first path segment.
// mutable://accounts/{pubkey}/* requires a signature from {pubkey}
// (or an explicitly granted key). Host-less locators throw.
const pubkeyAccess = createPubkeyBasedAccess();
// Combined: pubkey namespace + relative path access lists
const access = createCombinedAccess(readFn);
// Wire into rig as a program
const validate = authValidation(access);Encryption
Ed25519 signing, X25519 encryption, AES-GCM symmetric, and PBKDF2 key derivation. Shared with b3nd-core (Identity needs it).
import {
createAuthenticatedMessage,
decrypt,
encrypt,
generateEncryptionKeyPair,
generateSigningKeyPair,
sign,
verify,
} from "@bandeira-tech/b3nd-canon/encrypt";
// Sign a payload
const keys = await generateSigningKeyPair();
const signature = await sign(keys.privateKey, { action: "transfer" });
const valid = await verify(
keys.publicKeyHex,
{ action: "transfer" },
signature,
);
// Encrypt (X25519 ECDH + HKDF + AES-GCM, forward secrecy via ephemeral keys)
const encKeys = await generateEncryptionKeyPair();
const encrypted = await encrypt(
new TextEncoder().encode("secret"),
encKeys.publicKeyHex,
);
const plaintext = await decrypt(encKeys.privateKeyHex, encrypted);b3nd-data
Five behavior-named schemes — hash://, immutable://, mutable://,
signed://, encrypted:// — that put the infrastructure guarantee in the
data layer and let the application domain live in the path. Apps mount under
any base path; protocol modules ship shape, not scheme. Lineage runs from early
b3nd-sdk and firecat into the b3nd-data protocol shipped here.
Canon exposes the vocabulary as data (constants + inspection helpers) and ships the small set of utilities that go with it (base-path templating, decomposed-record paths). Enforcement is an app/operator concern — none of these helpers throw on "wrong" input; they return inspection results so callers compose their own rules.
import {
checkSchemeIdShape, // returns a reason string for thing://<opaque-id> shapes
dataUri,
entryUri, // <root>/{data|meta|entries}/...
interpolateBasePath, // mutable://{account?shared}/notes
isBehaviorScheme, // true for hash://, immutable://, mutable://, signed://, encrypted://
metaUri,
parseDecomposed,
SCHEMES, // { hash, immutable, mutable, signed, encrypted }
} from "@bandeira-tech/b3nd-canon/data";
// Schemes name behaviors (rules), not domains
const inbox = interpolateBasePath(
"encrypted://{account?anon}/inbox",
{ pubkey: "0xabc" },
);
// "encrypted://0xabc/inbox"
// Decomposed record paths separate canonical data, bookkeeping, and history
const root = `${SCHEMES.signed}0xabc/taskwatch/t/abc123`;
dataUri(root, "title");
// "signed://0xabc/taskwatch/t/abc123/data/title"
entryUri(root, "2026-06-19T20:00:00Z", "progress");
// "signed://0xabc/taskwatch/t/abc123/entries/2026-06-19T20:00:00Z-progress"See b3nd-skill notes/uri-scheme-shape.md and notes/base-path-injection.md
for the rationale.
Libraries
| Library | Description |
| -------------- | ----------------------------------------------------------------------- |
| b3nd-msg | Message envelopes, MessageData convention, program + handler |
| b3nd-auth | Pubkey-based access control, relative path access, signature validation |
| b3nd-binary | JSON-safe binary codec (Uint8Array / ArrayBuffer round-trip) |
| b3nd-data | Five behavior-named schemes, base-path templating, decomposed paths |
| b3nd-hash | Content addressing — re-exported from b3nd-core/hash |
| b3nd-encrypt | Ed25519 + X25519 + AES-GCM — re-exported from b3nd-core/encrypt |
Subpath Exports
import { ... } from "@bandeira-tech/b3nd-canon"; // msg + auth + binary + data
import { ... } from "@bandeira-tech/b3nd-canon/msg"; // message envelopes
import { ... } from "@bandeira-tech/b3nd-canon/auth"; // access control
import { ... } from "@bandeira-tech/b3nd-canon/binary"; // binary JSON codec
import { ... } from "@bandeira-tech/b3nd-canon/data"; // b3nd-data: schemes, templates, paths
import { ... } from "@bandeira-tech/b3nd-canon/hash"; // content addressing (from b3nd-core)
import { ... } from "@bandeira-tech/b3nd-canon/encrypt"; // signing + encryption (from b3nd-core)Development
deno task check # Type check (covers every subpath via mod.ts)
deno task test # Run libs/**/*.test.ts
deno task build:npm # dnt dual-publish output to ./npmProject Structure
*.ts # Subpath entry stubs (mod, msg, hash, auth, encrypt, binary, uri)
libs/ # In-repo libraries (msg, auth, binary, uri — hash and
# encrypt are re-exported from b3nd-core)Related
- b3nd-core -- framework foundation (types, rig, clients, network)
- b3nd-sdk -- SDK umbrella that re-exports core + canon
License
MIT
