Signing is where a key identity produces proof. TKeeper signs only after the command is accepted by the key's authorities and any configured policy, audit, lifecycle, and quorum controls.
Endpoints
POST /v2/keeper/sign
POST /v2/keeper/sign/verify
Required permissions:
tkeeper.key.{keyId}.sign
tkeeper.key.{keyId}.verify
Signing flow
command
-> authority match
-> intent materialization
-> policy and key controls
-> audit gate
-> mono or threshold signing
-> signature proof
For arbitrary, TKeeper signs bytes after checking the key identity allows arbitrary.
For concrete authorities, TKeeper loads the authority document, materializes the command into an intent, extracts effects where supported, and evaluates policy. It signs for ALLOW, or after every requirement from ALLOW_WITH_REQUIREMENTS is satisfied.
Quorum modes
| Mode | Behavior |
|---|---|
mono |
signs locally with full key material after controls pass |
threshold |
coordinator starts a threshold protocol; private key is not reconstructed |
Threshold signing protocols:
| Scheme | Threshold protocol |
|---|---|
ECDSA |
GG20 |
EdDSA |
FROST |
SCHNORR |
FROST |
BIP340 |
FROST |
TAPROOT |
FROST |
MLDSA |
threshold ML-DSA |
Commands
Arbitrary command:
{
"keyId": "demo-identity",
"command": {
"type": "arbitrary",
"authorityId": "arbitrary",
"artifact": {
"scheme": "ECDSA",
"hash": "SHA256",
"data64": "aGVsbG8="
}
}
}
Typed command:
{
"keyId": "deployment-signing",
"command": {
"type": "custom",
"authorityId": "production-deployment",
"artifact": {
"scheme": "ECDSA",
"hash": "SHA256",
"typed": {
"action": "deploy",
"service": "billing-api",
"environment": "production",
"releaseVersion": "2.3.1",
"sequence": 10000000000000000001,
"riskScore": 0.20,
"roles": ["release-manager", "production"],
"sourceIp": "10.20.4.17",
"requestedAt": "2030-01-02T03:04:05Z",
"expiresAt": "2030-01-02T03:09:05Z",
"changeProof": "AQIDBA=="
}
}
}
}
See the typed authority example for the matching schema, payload, effects, and policy. EVM, Bitcoin, and X.509 commands are documented on their intent-specific pages.
Schemes and algorithms
| Scheme | Mono | Threshold | Algorithms |
|---|---|---|---|
ECDSA |
local ECDSA | GG20 | SECP256K1, P256 |
SCHNORR |
not supported | FROST | SECP256K1, P256 |
BIP340 |
local BIP340 | FROST | SECP256K1 |
TAPROOT |
local Taproot key-path | FROST | SECP256K1 |
EdDSA |
local EdDSA | FROST | ED25519 |
MLDSA |
local ML-DSA | threshold ML-DSA | MLDSA44, MLDSA65, MLDSA87 |
Hash methods:
| Hash | Meaning |
|---|---|
NONE |
sign bytes as-is |
SHA256 |
hash before signing |
SHA512 |
hash before signing |
KECCAK256 |
hash before signing |
The command artifact decides the scheme and hash. The top-level sign request does not carry hash or algorithm.
Response
{
"type": "ECDSA",
"signature64": "...",
"generation": 1,
"imposters": []
}
imposters is meaningful for threshold protocols. Mono signatures return an empty list.
Verify response:
{ "valid": true }
generation is optional on verify. If omitted, TKeeper uses the active generation.
Verify is purely cryptographic. Its command contains only type and artifact; it does not contain authorityId, load an authority manifest, or evaluate policy. A caller may verify any supported material type, including custom typed data or arbitrary bytes, even when that type or payload would not be authorized for signing by the key's current manifest. The type still selects structural material validation and canonical serialization before the signature is checked, and a successful result proves only cryptographic validity, not current policy authorization.
{
"keyId": "payments-key",
"generation": 1,
"command": {
"type": "custom",
"artifact": {
"scheme": "ECDSA",
"hash": "SHA256",
"typed": {
"amount": 100,
"currency": "USD"
}
}
},
"signature64": "..."
}
ML-DSA availability
Threshold ML-DSA uses probabilistic rejection sampling. A complete attempt can abort even when peers are healthy. TKeeper retries with fresh session state up to:
keeper.session.mldsa.max-rounds
The default is 12. Exhaustion returns SESSION_MAX_ROUNDS_EXCEEDED; treat it as availability first, not proof of corruption.
Downstream verification
The downstream system should verify that the proof matches the exact command it is about to execute and the identity it intended to trust.
Signature validity alone is insufficient. The acceptance contract should cover:
- expected key identity or public key
- canonical command and every field that changes the effect
- generation and tweak context required by the integration
- environment or domain separation between test and production
- nonce, expiry, sequence, or idempotency where replay matters
If context is enforced outside the signed payload, the verifier must reject mismatches before execution. For arbitrary, TKeeper governs only the supplied bytes; it cannot infer omitted business context.
Common failures
Verify returns false
Check that the command type and artifact, tweak, generation, and signature64 match the signed material. authorityId is signing policy context and is not part of Verify. For arbitrary commands, also check hash and scheme inside the command artifact.
Authority rejects the command
AUTHORITY_VIOLATION means the command selected an authority that is not attached to the key. INVALID_AUTHORITY_ARTIFACT means the command shape does not match the selected authority or its feature is absent. POLICY_VIOLATION means the materialized intent was understood but policy returned DENY.
No manager for scheme and algorithm
The key algorithm or quorum mode does not support the requested signature scheme.