SmartSigner (Signing)
Ed25519 key generation, signer/key-page version lookups, and the sign→submit→wait flow every write operation in every other resource internally goes through — exposed here as its own first-class, workflow-visible resource.
Why signing became its own resource
Originally proposed as cross-cutting infrastructure only — internal plumbing every write operation
calls into, never exposed to a workflow author directly — the project owner overruled that on
2026-07-02 and asked for Signing to be a first-class resource. SignerFactory
(Services-layer plumbing) still exists and is still used internally by every other resource's write
operations to resolve a credential into a SmartSigner instance. What changed is that
key generation and signer/version querying (SS01/SS02) are now directly workflow-visible operations
in their own right, not merely internal mechanics.
Per 004.References/AboutAccumulate.md §2, Accumulate signs with Ed25519,
not the secp256k1/ECDSA scheme most EVM chains use — an Ethereum key or wallet is not compatible
here. And because a Key Page carries a version number that must match what a signature references
to validate, SmartSigner's whole reason to exist is automatically tracking and
incrementing that version — solving exactly the "stale version gets rejected" problem SS02 also
exists to diagnose standalone.
Operations
| Code | Operation | Maps to | Why a workflow author wants it | Priority |
|---|---|---|---|---|
| SS01 | Generate Signing Keypair | AccKeyPairGenerator.GenerateSignatureKeyPair(SignatureType.ED25519) |
Provision a brand-new Ed25519 keypair as part of an onboarding workflow, e.g. before creating a Lite Identity or Key Page | MVP |
| SS02 | Query Signer / Key Page Version | KeyManager (+ V2 query / query-key-index) |
Look up a key page's current signer set, threshold, and version before signing — the exact problem SmartSigner exists to solve; also useful standalone for diagnostics (was KM08) |
MVP ★ |
| SS03 | Sign, Submit & Wait (generic) | SmartSigner.SignSubmitAndWaitAsync(principal, body) |
Raw escape hatch: sign and submit an arbitrary TxBody and wait for delivery, for transaction types without their own named operation yet |
Phase 2 |
| SS04 | Sign and Proceed (Multi-Sig Co-Sign) | SmartSigner co-signing a pending transaction |
Adds this signer's Ed25519 signature to an already-pending multi-sig transaction. Decided 2026-07-02: this is the ONLY multi-sig operation the node needs | MVP |
| SS05 | Submit Pre-Signed Transaction (Bring Your Own Signature) | V2/V3 execute / execute-direct (submits an already-signed envelope as-is — no TxBody construction, no SmartSigner signing) |
Added 2026-07-02 (owner): lets an external signer (their own wallet, their own key custody, outside this node entirely) sign a transaction independently and hand the node the finished, already-signed envelope. The node's job is purely to submit it and recognize/parse the transaction's parameters so the workflow can branch and continue based on what was actually submitted — not to hold or use any key at all | MVP |
Every other write operation in this document (ID01–ID02, TA01–TA07,
DA01–DA03, KM01–KM07, CR01, SS03, SS04) is custodial — the node
resolves a credential from BizFirst's vault and signs on the caller's behalf, per the
platform's mandatory Credential Pattern. SS05 is the deliberate exception: the private key
never touches this node or the credential vault at all. The signer does their own signing
entirely outside BizFirst and only ever hands the node a finished, already-signed
transaction. This also answers/subsumes the "raw execute escape hatch" open
question below — execute/execute-direct is exactly the mechanism
SS05 needs, since those methods take a pre-built signed envelope and just relay it, doing no
signing of their own.
See the dedicated Signing Pathways design note for how HIL approval and credential choice interact with SS04 and SS05 — auto-sign, HIL approval with a vault-resolved credential, or HIL approval where the approver's reply already carries its own signature and flows straight into SS05.
How it works — sign, submit, wait 3 seconds
The SDK's headline feature bundles "build → sign → submit → poll for delivery" into one async call, because Accumulate transactions aren't finalized in a single step — a synthetic transaction settles the cross-ADI effect afterward. The project owner's 2026-07-02 decision fixes the sync/async boundary at 3 seconds: if the transaction confirms within that window, the operation returns as a normal synchronous success ("in case it signs we can continue as is," in the owner's words); if not, it returns pending rather than blocking further.
sequenceDiagram
participant WF as Workflow / any write operation
participant SF as SignerFactory
participant SS as SmartSigner
participant NET as Accumulate Network
WF->>SF: resolve credentialId via ICredentialResolver
SF->>SS: new SmartSigner(client.V3, keypair, signerUrl)
WF->>SS: SignSubmitAndWaitAsync(principal, TxBody)
SS->>SS: build envelope, sign (Ed25519)
SS->>NET: submit transaction
NET-->>SS: poll for delivery (up to 3s window)
alt confirmed within 3s
SS-->>WF: synchronous success — treat as done
else not yet confirmed
SS-->>WF: pending status — treat as pending, check later
WF->>WF: follow up via QX01 / QX03 / TRIG01
end
Every write operation across the whole node that submits a transaction (ID01–ID02,
TA01–TA07, DA01–DA03, KM01–KM07, CR01, SS03, SS04 — anything going through
SmartSigner) follows this same 3-second rule. Needs confirmation at implementation
time against how SmartSigner.SignSubmitAndWaitAsync()'s own internal polling
behaves — the SDK's "wait for delivery" may need an explicit timeout/cancellation parameter to
actually enforce a 3-second cap rather than blocking until the SDK's own poll loop finishes on
its own schedule.
How it works — multi-sig, consolidated through ApprovalNode
The node does not build its own pending-transaction tracking, threshold counting,
or a separate long-running "wait for N signers" operation. The platform already has
ApprovalNode (BizFirst.Ai.ExecutionNodes.Core) with an
eApprovalStrategy enum that includes NofM alongside All/AnyOne
— exactly the consolidation mechanic Accumulate's async multi-sig needs.
flowchart TD T["Pending multi-sig transaction
submitted against a 2-of-3 Key Page"] --> AN["ApprovalNode step
strategy = NofM, one actor per required signer"] AN --> A1["Approver A action"] AN --> A2["Approver B action"] AN --> A3["Approver C action (not needed — threshold already met)"] A1 --> SS4a["SS04 Sign and Proceed
adds Approver A's Ed25519 signature"] A2 --> SS4b["SS04 Sign and Proceed
adds Approver B's Ed25519 signature"] SS4a --> R["ApprovalNode suspend/resume framework
(not this node) waits for threshold"] SS4b --> R R --> DONE["2-of-3 threshold met — workflow resumes"] QX3["QX03 Query Pending Transaction
(optional status check, not required for core flow)"] -. available on demand .-> T
Stated in the design proposal as a general rule for the rest of the design too: any future
operation that needs "wait for multiple humans/systems to act before proceeding" semantics
should default to composing with ApprovalNode rather than this node growing its
own suspend/consolidate logic. HierarchyProvisioner (ID05, see
Identity) is a different case — SDK-side idempotent
provisioning, not human consolidation — so it stays a direct SDK wrap.
Business use case — cryptographic, non-repudiable approval
001.BusinessIdeas/README.md §2.2Each approver's action invoking SS04 to add their signature to the pending transaction is what turns "recorded who clicked approve" into "produced a cryptographic signature against a defined authority structure." That distinction matters for exactly the customer who might need to defend an approval decision to a party that doesn't implicitly trust BizFirst's own database — a regulator, an auditor, a counterparty in a dispute.
Open questions affecting this resource
-
Largely resolved by SS05, but not struck through in §5 · Raw "execute" escape hatch
The V2 surface includes generic
execute/execute-direct/execute-localmethods alongside named per-type methods. The SS05 design note states this "answers/subsumes" the raw-execute question, sinceexecute/execute-directis exactly the mechanism SS05 needs. The design proposal's own §5 open-questions list has not yet been edited to strike this bullet, so it's presented here as effectively answered in practice, with the source document's own bookkeeping still catching up. -
Open · Does the SDK support decoding a pre-signed envelope back into structured parameters?
SS05's "recognize/parse the transaction's parameters" requirement needs decoding an arbitrary signed envelope back into a structured, typed result — the reverse of what
TxBody's factory methods do (encoding).AboutAcmeNetSdk.mdonly documents the SDK's encode-sideTxBodyfactory; it does not confirm a corresponding decoder. The SDK's GitHub README does list aCodec/namespace ("binary TLV encoding") which may cover this, but that was not independently verified against source. This needs confirmation before SS05 can be implemented — if the SDK can't decode an arbitrary envelope, this node would need its own TLV parser, meaningfully more implementation work than every other operation here (all of which only ever encode, never decode arbitrary input). -
Unverified · is "SmartSigner" a protocol concept or an SDK abstraction?
Per
004.References/AboutAccumulate.md, this "smart" version-tracking behavior is described only as anAcme.Net.Sdkfeature/abstraction, not confirmed as an official Accumulate protocol term — worth confirming directly withjason_gregoireor the SDK source if the distinction matters for the ExecutionNode's design.