Resource · 2.5 in the design proposal · added 2026-07-02 per owner decision

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.

5 operations 4 MVP candidates 1 Phase 2+ (SS03)

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

CodeOperationMaps toWhy a workflow author wants itPriority
SS01Generate Signing KeypairAccKeyPairGenerator.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
SS02Query Signer / Key Page VersionKeyManager (+ 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
SS03Sign, 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
SS04Sign 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
SS05Submit 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
Non-custodial submission (SS05) — the deliberate exception

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.

SS03 / the internal flow every write op shares
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
      
Settled 2026-07-02 — 3-second transaction confirmation wait policy

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.

SS04 + ApprovalNode NofM — the intended multi-sig pattern
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
Settled 2026-07-02 — a general principle, not just a multi-sig special case

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

From 001.BusinessIdeas/README.md §2.2

Each 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