Skip to main content
Every account carries a multisig configuration. By default it is a single Ed25519 master key that can authorize anything. Initial setup can include weighted co-signers and per-tier thresholds. After setup, the public OMS runtime write path is add-only: POST /multisig/add-signer can add co-signers, but signer removal, threshold changes, and recovery flows are not available yet. A request is authorized when the combined weight of its valid signatures meets the threshold for that request’s action tier. Multisig is checked at the OMS before submission and re-enforced at the clearing engine, so a compromised gateway cannot bypass it.

Signers and weights

A configuration has three parts:
  • Master key - the account’s Ed25519 key, counted at master_weight.
  • Additional signers - zero or more co-signer keys, each with its own weight and optional time limits.
  • Thresholds - three integers, low / medium / high, one per action tier.
Each additional signer entry: The master key is just the heaviest signer by default; it has no powers beyond its weight. A request can be authorized by co-signers alone, and with master_weight set to 0 the master key contributes nothing toward any threshold. Default. A new account has master_weight = 1, no additional signers, and all three thresholds 1 - so the master key alone clears every tier. Anti-lockout. A configuration is rejected unless every threshold is at least 1 and the total weight of all keys is at least the high threshold, so the configured keys can always satisfy the strictest tier. The master key cannot also be listed as an additional signer, and no key may appear twice. This guards against the obvious lockout; it cannot stop you assigning weight to a key you do not actually hold.

Action tiers

Every request type is fixed to one of three tiers. A request passes when the summed weight of its valid signers is greater than or equal to that tier’s threshold. You will normally set low <= medium <= high so value-moving actions need more signatures than trading. The protocol does not enforce that ordering - only that every threshold is at least 1 and that the combined weight of all your keys covers the high threshold.

Signer key types

Four schemes are supported. The master key is always Ed25519; co-signers may be any scheme. Every signer - master and co-signers alike - signs the same payload: the request’s inner object serialized to compact JSON, with whitespace stripped and fields in declaration order. The signature is over the raw payload bytes, not a hash. ECDSA signatures (secp256k1 and P-256) are normalized to low-s form on verification, so signature malleability cannot be used to produce a distinct-but-valid signature. WebAuthn co-signers submit a full assertion - authenticatorData, clientDataJSON, and a P-256 signature over authenticatorData || SHA256(clientDataJSON). Tplus checks the user-present flag, that the assertion type is webauthn.get, the relying-party ID and origin, and that the assertion’s challenge equals the request payload. That last check binds a passkey signature to exactly one request.

Signing a request

A signed request carries the master signature and any co-signatures separately:
signature holds the master Ed25519 signature bytes, or is empty when co-signers authorize instead. Each additional_signers entry pairs a signer - tagged by scheme (Ed25519, Secp256k1, P256, or WebAuthn) - with its signature, and is matched to a registered signer by exact key. Verification:
  • Each co-signer must be registered on the account, unexpired, and within its signing window, and its signature must verify over the payload. An unknown, duplicate, expired, or out-of-window signer rejects the whole request.
  • The master signature, if present, adds master_weight; each valid co-signer adds its entry’s weight.
  • If the total is below the tier threshold, the request is rejected with an insufficient-weight error.
For orders, co-signers ride in the same additional_signers array and are validated when the order is finalized, with the signing-window check measured against the order’s creation time.

Configuring multisig

Initial setup is onchain, at first deposit. A first-time depositor calls the deposit overload that also registers a configuration: masterWeight, the three thresholds, and an array of SignerConfig entries (key, keyType, keyPrefix, weight, expiresAtNs, maxSigningWindowNs), plus a masterConfigSignature - the master key’s signature over the configuration. The clearing engine applies it once and ingests it into the ledger; the plain deposit leaves the default single-key config in place. The OMS exposes these runtime endpoints. Only POST /multisig/add-signer is a supported multisig write. Successful /multisig/add-signer requests are replay-protected by config_nonce - a monotonic counter supplied as ce_nonce on each change and bumped on success. There are no public OMS endpoints yet for removing or disabling signers, changing thresholds, or recovering a multisig configuration. See signing and replay protection for where multisig sits in the trust model.