ma-core
A lean DIDComm service library for the ma ecosystem.
ma-core provides everything an ma endpoint needs: DID document publishing,
service inboxes, outbox delivery, and transport abstraction — without coupling
to any specific runtime or application.
What it provides
Messaging primitives
Inbox— bounded, TTL-aware FIFO receive queue for service endpoints. Per-message TTL is computed from each message'screated_at + ttl. Only endpoint implementations push to an inbox; consumers read viapop/peek/drain.- Outbound delivery — transport-agnostic fire-and-forget send path. Messages are validated, serialized to CBOR, and transmitted.
Service model
Servicetrait — declares a protocol identifier and label.MaEndpointtrait — shared interface for all transport endpoints: register services, get inboxes, send messages.
The crate currently ships with an iroh-based transport backend internally, but iroh-specific types are considered backend details.
Every endpoint must provide ma/inbox/0.0.1. Endpoints may optionally
provide ma/ipfs/0.0.1 to publish DID documents on behalf of others.
DID document publishing
validate_ipfs_publish_request— decodes a signed CBOR message, enforcesapplication/x-ma-ipfs-requestcontent type, validates the document, verifies sender matches IPNS identity.IpfsDidPublisher(non-WASM,kubofeature) — publishes validated documents to IPFS via the native RPC backend.publish_did_document_to_kubo/handle_ipfs_publish— lower-level publish helpers.
DID resolution
DidDocumentResolvertrait — async DID-to-Document resolution.IpfsGatewayResolver— resolves via an IPFS/IPNS HTTP gateway.
Transport parsing
Parses DID document service strings like /iroh/<endpoint-id>/ma/inbox/0.0.1:
endpoint_id_from_transport/protocol_from_transportresolve_endpoint_for_protocol/resolve_inbox_endpoint_idtransport_string— build service strings from parts.
Identity bootstrap
generate_secret_key_file/load_secret_key_bytes— secure 32-byte key persistence with OS-level permission hardening.
Pinning
pin_update_add_rm— pin new CID, unpin old, report unpin failures as metadata (not hard errors).
Native IPFS RPC (non-WASM, kubo feature)
HTTP client for /api/v0/ endpoints: add, cat, DAG put/get,
IPNS publish/resolve, key management, pinning.
Feature flags
These are Cargo compile-time feature flags.
| Feature | Default | Description |
|---|---|---|
kubo |
no | Native IPFS RPC backend for publishing |
iroh |
yes | Internal iroh QUIC transport backend |
gossip |
yes | Internal iroh gossip support |
config |
no | Config model + YAML serialization + encrypted secret bundles (CLI/fs/logging remain native-only) |
config feature
The config feature supports both native and wasm32 targets, but with
different capability levels.
It provides on all targets:
Config— serializable config model (from_yaml_str,to_yaml_string).SecretBundle— generate keys and encrypt/decrypt bundle bytes.BrowserIdentityExport— JSON payload with inlined encrypted bundle (encrypted_secret_bundle_base64) for browser import/export.
Native-only additions:
MaArgs— a#[derive(Args)]struct you flatten into your ownParser.Config::from_args— merge CLI/env/YAML/defaults for daemons.Config::save/Config::gen_headless— filesystem persistence helpers.SecretBundle::save/SecretBundle::load— encrypted file I/O.Config::init_logging()— sets uptracing-subscriberwith separate log levels for file and stdout.
Wasm logging behavior:
Config::init_logging()is also available on wasm and routes logs to browser console.- Console output is filtered by
log_level_stdout.
Minimal usage:
use Parser;
use ;
const MA_DEFAULT_SLUG: &str = "myd";
Config file ($XDG_CONFIG_HOME/ma/<slug>.yaml) example:
log_level: debug
kubo_rpc_url: http://127.0.0.1:5001
did_resolver_positive_ttl_secs: 60
did_resolver_negative_ttl_secs: 10
Platform support
Core types (Inbox, Service, transport parsing, validation)
compile on all targets including wasm32-unknown-unknown.
- This library is intended for both wasm and native targets.
IpfsGatewayResolveris available on both wasm and native for gateway-based DID fetch.- Only Kubo write/pin operations are native-only.
- On wasm builds, the native
kubomodule is not compiled in. configmodel serialization andSecretBundlecrypto are available on wasm.configfilesystem and CLI/env facilities are native-only.irohtransport compiles on wasm and native.gossipis optional and can be enabled when needed.
Important: ma-core does provide gateway-based DID fetch on wasm via
IpfsGatewayResolver. Native-only IPFS RPC operations (publish/pin/write)
remain unavailable on wasm.
For wasm storage, persist encrypted SecretBundle bytes and serialized Config
text in browser storage, and provide the passphrase from user input at runtime
instead of storing it.
Compile-time split note:
- On wasm,
Configdoes not include Kubo-specific fields. - On native,
Configincludes daemon/Kubo fields and filesystem helpers.
Quick usage
Consumers receive validated Message objects from an Inbox — the endpoint
handles deserialization and validation before messages enter the queue:
// Endpoint gives you an Inbox<Message> when you register a service
let mut inbox = endpoint.service;
let now = current_time_secs;
while let Some = inbox.pop
Example: full publish flow against native IPFS RPC (native only):
async
End-to-end operational flow
This section shows a concrete native-only flow for publishing a DID document through native IPFS RPC.
1. Preconditions
- Native IPFS RPC API is reachable at whichever base URL your environment uses.
- Create a publisher once with that URL and reuse the same instance.
- You have a signed CBOR
Messagepayload wherecontent_typeisapplication/x-ma-ipfs-request,fromis a DID whose IPNS id matches the DID document id, andcontentis CBOR encodedIpfsPublishDidRequest. - The DID document is valid and signature-verifiable.
The DID document itself is always DAG-CBOR. Use Document::encode() and
Document::decode() for serialization.
2. Validate and publish
Use IpfsDidPublisher when you want a persisted endpoint configuration:
pub async
What it does internally:
validate_ipfs_publish_requestverifies message and document integrity.- Publisher imports the IPNS key under an ephemeral name, publishes, and removes the key immediately after.
- Returns
IpfsPublishDidResponsewithdidandcid.
3. Verify published target
After publish, resolve the IPNS name and compare to expected CID path:
pub async
4. Production readiness pattern
Recommended startup/publish order:
- create
IpfsDidPublisher::new(kubo_url)once - call
publisher.wait_until_ready(attempts) - decode transport bytes
- call
publisher.publish_signed_message(...) - emit structured log with
did,cid - optionally run a post-publish
name_resolvecheck
Minimal orchestration example:
pub async
5. Failure semantics you should rely on
- Invalid CBOR, content type, DID document, or signatures fail fast.
- Sender/document IPNS mismatch fails fast.
- Missing/import-mismatched key material fails fast.
- IPNS publish uses retry and may still be accepted if resolve confirms target.
- Unpin failures in pin rotation are returned as metadata (
PinUpdateOutcome) and do not hide successful new pin operations.
Build and test
Wasm, slim iroh-only profile:
Note: target-specific wasm dependencies in Cargo.toml enable required web RNG support (getrandom/js) automatically for wasm32-unknown-unknown.
Wasm, iroh + gossip profile (when you need broadcast):
Run clippy when needed:
Design principles
- Strict input validation; never mutate malformed data.
- Small and clear building blocks, without unnecessary complexity.
- Shared types and contracts in the library; avoid duplication in consumers.
- Fail hard when identity, signature, or key mapping does not match.