Skip to content

`role-approvals` State Machine

role-approvals is Yano’s application-neutral authorization product for cases where business actors are not the app-chain validator members. Governed organizations, actors, public keys, roles and policies authorize an exact payload hash; the finalized proposal and decision trail are committed under the app-chain state root.

It is bundled for JVM and native distributions and currently has preview maturity.

Use role-approvals when:

  • an employee, auditor, regulator, service or device must sign independently of the member node relaying its command;
  • a policy needs role checks, minimum counts, or distinct organizations;
  • actor keys and policies must rotate through governed revisions; and
  • the application can act on a provably approved payload hash.

Use approvals instead when validator members themselves are the approvers. Use role-evidence when you specifically need Yano’s complete evidence release and publication workflow. Use a reviewed composite plugin when approval must atomically drive another deterministic domain transition.

The generic machine does not store arbitrary payload bytes, execute them, or emit an effect. An approval proves authorization of (payloadDomain, payloadHash); the application remains responsible for binding those bytes to its next action.

From an extracted release or the source app/ directory:

Terminal window
./yano.sh appchain init --non-interactive \
--recipe role-approval \
--network devnet \
--members 3 \
--runtime jvm \
--deployment host \
--name role-approval-chain \
--chain-id role-approval-chain \
--output role-approval-chain

Add the reviewed public member keys to the generated blueprint, render, and validate it:

Terminal window
./yano.sh appchain render role-approval-chain
./yano.sh appchain config validate --mode project role-approval-chain
./yano.sh appchain doctor role-approval-chain --distribution .

The generated bootstrap/role-approvals-plan.yaml is a non-secret plan for organizations, actors, proof-of-possession, policies, governance and verification. Replace its placeholders with public values only. Private actor keys belong in the actor application, KMS, HSM or vault—not in Yano, Studio, the blueprint or the bootstrap plan.

Bootstrap operations are fail-closed and idempotent:

  1. Query the committed record and verify its proof.
  2. If the exact revision/value already exists, record the proof and skip it.
  3. If it is absent, submit PROPOSE, collect member approvals to the configured threshold, then ACTIVATE.
  4. If an existing revision or value differs, stop; never replace it silently.
outer AppMessage sender = consortium member that relayed the command
signed actor identity = business actor authorizing the payload hash

REST authentication only controls access to an endpoint. It is neither a member vote nor a business-actor signature.

An actor statement binds its action, chain, proposal, policy revision, payload domain/hash, deadline, actor revision, key and policy clause. A valid signature therefore cannot be replayed for a different chain, payload, role decision or policy revision.

PurposeTopic or committed query
Govern organizations, actors and keysactors.command.v1
Submit actor proposals and decisionsrole-approvals.command.v1
Current organizationcomponents/domain-actors/organization-current, params id
Organization revisioncomponents/domain-actors/organization, params id@revision
Current actorcomponents/domain-actors/actor-current, params id
Actor revisioncomponents/domain-actors/actor, params id@revision
Current policycomponents/role-approvals/policy-current, params id
Policy revisioncomponents/role-approvals/policy, params id@revision
Proposalcomponents/role-approvals/proposal, params proposalId
Proposal statisticscomponents/role-approvals/stats, empty params

The physical state keys are namespaced by the committed composite profile. Use the proof key returned by the domain API rather than constructing a physical key from a display identifier.

Hash the exact canonical application bytes before asking actors to sign them. The payload domain must identify that byte contract, for example com.example.order.v1.

Terminal window
PAYLOAD_HASH=$(openssl dgst -sha256 -binary approved-order.cbor | xxd -p -c 256)
COMMAND_HEX=$(./yano.sh appchain role sign \
--action approve \
--chain role-approval-chain \
--proposal order-a-1001 \
--policy order-release \
--policy-revision 1 \
--payload-domain com.example.order.v1 \
--payload-hash "$PAYLOAD_HASH" \
--deadline-height 1000 \
--actor reviewer-a \
--actor-revision 1 \
--key reviewer-key-v1 \
--clause reviewers \
--seed-file /owner-only/reviewer.seed)

Submit the canonical CBOR through any member. The member relays the command; the embedded actor signature determines whose decision is evaluated:

Terminal window
curl -sS -X POST \
http://127.0.0.1:7070/api/v1/app-chain/chains/role-approval-chain/messages \
-H 'Content-Type: application/json' \
-H "X-API-Key: $YANO_APPCHAIN_API_KEY" \
-d "{\"topic\":\"role-approvals.command.v1\",\"bodyHex\":\"$COMMAND_HEX\"}" | jq .

HTTP 202 means accepted for sequencing, not approved. Wait for finality and query the proposal.

The generic bundle exposes read-only JSON projections:

Terminal window
BASE=http://127.0.0.1:7070/api/v1
BUNDLE=com.bloxbean.cardano.yano.appchain.role-workflow
curl -sS \
-H "X-API-Key: $YANO_APPCHAIN_API_KEY" \
"$BASE/plugins/$BUNDLE/proposals/order-a-1001?chain=role-approval-chain" | jq .
curl -sS \
-H "X-API-Key: $YANO_APPCHAIN_API_KEY" \
"$BASE/plugins/$BUNDLE/stats?chain=role-approval-chain" | jq .

Organization, actor and policy routes follow the same pattern:

organizations/{id}?chain={chainId}[&revision=N]
actors/{id}?chain={chainId}[&revision=N]
policies/{id}?chain={chainId}[&revision=N]

A response includes committedHeight, stateRoot, proofKey and recordValue. When the route resolves a current revision, it also returns the current-pointer proof key/value. Verify both proofs at the same height/root: the record proof establishes that a revision exists; the pointer proof establishes that it was current.

Use the dependency-light contracts plus the generic client:

implementation "com.bloxbean.cardano:yano-x-client:${yanoVersion}"
implementation "com.bloxbean.cardano:yano-x-role-workflow-contracts:${yanoVersion}"
var statement = new ActorStatementV1(
ActorStatementV1.Action.APPROVE,
"role-approval-chain", "order-a-1001", "order-release", 1,
"com.example.order.v1", payloadHash, 1000,
"reviewer-a", 1, "reviewer-key-v1", "reviewers");
byte[] command = SignedActorCommandV1.sign(statement, actorSeed).encode();
var client = AppChainClient.builder("http://127.0.0.1:7070/api/v1")
.chainId("role-approval-chain")
.apiKey(System.getenv("YANO_APPCHAIN_API_KEY"))
.build();
var submitted = client.submit("role-approvals.command.v1", command);
System.out.println(submitted.messageId());

Production code should implement ActorStatementV1.signingPreimage() in its KMS/HSM signer rather than loading a raw seed into application memory.

  • Roles are normalized strings, not a fixed evidence enum.
  • Policies support proposer roles, bounded AND clauses, minimum counts, distinct actors or organizations, rejection behavior and lifetimes.
  • Actor and policy revisions are immutable; current pointers are governed.
  • V1 retains at most 16 key epochs per actor.
  • Pending proposals and governed mutations are bounded.
  • Invalid, stale, ineligible or wrongly signed finalized commands are deterministic no-ops; verify resulting state rather than message inclusion.
  • Proposal expiration is materialized when a later command touches the pending proposal after its block-height deadline; there is no unbounded global scan.

The stock generic profile deliberately emits no effect because an approved hash is not executable payload bytes. An application can:

  1. query the terminal proposal and act idempotently off chain;
  2. submit a separately validated domain command bound to the proposal; or
  3. install a reviewed composite profile that atomically consumes the approval.

Do not attach an arbitrary executor and assume it knows which bytes were approved. A new automatic effect or domain transition needs a versioned contract that binds exact executable bytes to the approved hash.

  • Monitor pending, rejected and expired proposal counts.
  • Rotate keys by governing the next actor revision with proof-of-possession.
  • Suspend or revoke compromised actors, then govern cancellation of affected pending proposals.
  • Historical approved decisions remain immutable after rotation or revocation.
  • Treat a changed profile digest, component order, route or state identity as a consensus upgrade requiring governed activation or a fresh chain.

For the complete wire, governance and recovery model, see Domain Actors and Role-Aware Approvals, the portable contracts, and the generic implementation guide.