Callback phê duyệt
Trước khi tham gia bất kỳ phiên nào, cosigner gửi một yêu cầu có chữ ký đến máy chủ của bạn và chờ nhận một approve có chữ ký. Máy chủ của bạn nhận được dữ liệu byte thô cần ký, nhờ đó có thể xác minh giao dịch độc lập với Fystack.

Cách hoạt động
- Cosigner xác minh
session_startcủa orchestrator. - Cosigner gửi
POSTmột JWT RS256 đến URL của bạn (Content-Type: application/jwt). - Máy chủ của bạn xác minh JWT, chạy chính sách và trả về
200kèm một JWT RS256. - Cosigner xác minh phản hồi và
request_id, sau đó tham gia phiên.
Yêu cầu được gửi cho KEYGEN, SIGN và RESHARE. Hãy áp dụng chính sách giao dịch cho SIGN.
Cosigner mặc định từ chối khi có lỗi (fail closed). Từ chối, phản hồi khác 200, chuyển hướng, hết thời gian chờ, body quá lớn, chữ ký sai hoặc request_id sai đều có nghĩa là không ký. Phiên sẽ thất bại với CALLBACK_REJECTED.
Thiết lập
Hai cặp khóa RSA-4096. Chỉ khóa công khai được trao đổi.
- Khóa cosigner: Được tạo bởi
cosigner-cli, dùng để ký yêu cầu. Cung cấpcosigner-public.pemcho máy chủ của bạn. - Khóa máy chủ: Được tạo trên máy chủ của bạn hoặc trong KMS, dùng để ký phản hồi. Cung cấp
server-public.pemcho cosigner.
Cùng host: trỏ đến thư mục khóa của máy chủ. CLI sẽ đọc server-public.pem và ghi cosigner-public.pem. URL mặc định là http://host.docker.internal:8089/cosigner/approve.
cosigner-cli callback add ./node-callback-server/keys
Máy chủ từ xa:
cosigner-cli callback add https://risk.example.com/cosigner/approve \
--server-public-key ./server-public.pem
CLI in ra đường dẫn và fingerprint của cosigner-public.pem; hãy cài đặt tệp này trên máy chủ của bạn. Thêm --ca ./ca.pem nếu dùng CA riêng.
callback add cập nhật config.yaml, đặt require_callback: true và khởi động lại cosigner (bỏ qua bằng --no-restart). cosigner-cli info hiển thị URL và fingerprint hiện tại.
config.yaml
approval:
require_callback: true
callback:
url: https://risk.example.com/cosigner/approve
timeout: 30s
retry_count: 1
private_key:
type: file
config:
path: /run/secrets/cosigner-private.der.hex
response_public_key_path: /run/secrets/server-public.pem
# ca_cert_path: /run/secrets/callback-ca.pem
- Name
- approval.require_callback
- Type
- boolean
- Description
Từ chối mọi phiên nếu không có callback. Giữ
truetrên production.
- Name
- callback.url
- Type
- string
- Description
Chỉ hỗ trợ HTTPS. Cho phép HTTP thường với
host.docker.internal./cosigner/approvesẽ được nối thêm nếu không có đường dẫn.
- Name
- callback.timeout
- Type
- duration
- Description
Tổng thời gian cho phép, bao gồm cả lần thử lại. Đồng thời là thời hạn của JWT yêu cầu. Mặc định
30s.
- Name
- callback.retry_count
- Type
- integer
- Description
Số lần thử lại khi gặp lỗi mạng hoặc
5xx, dùng cùng token. Tối đa và mặc định là1.
- Name
- callback.private_key
- Type
- secret provider
- Description
Khóa ký RSA-4096 của cosigner, định dạng hex PKCS#8 DER.
- Name
- callback.response_public_key_path
- Type
- string
- Description
Khóa công khai RSA-4096 của máy chủ của bạn (PEM).
- Name
- callback.ca_cert_path
- Type
- string
- Description
Chứng chỉ CA cho chứng chỉ máy chủ riêng hoặc tự ký.
Yêu cầu
JWT header {"alg": "RS256", "typ": "JWT"}. Chỉ chấp nhận RS256 và xác minh bằng cosigner-public.pem trước khi đọc các claim.
- Name
- request_id
- Type
- string (UUID)
- Description
Duy nhất cho mỗi yêu cầu, đồng thời là
jti. Hãy trả lại giá trị này. Dùng làm khóa idempotency.
- Name
- iat, exp
- Type
- number
- Description
exp=iat+ timeout.
- Name
- cosigner_id, workspace_id, participant_id
- Type
- string
- Description
Định danh của cosigner.
- Name
- session_id, orchestrator_id
- Type
- string
- Description
Phiên và orchestrator đã khởi tạo phiên đó.
- Name
- coordinator_sequence, coordinator_signature
- Type
- number, string
- Description
Chữ ký orchestrator đã được xác minh. Ghi log để truy vết.
- Name
- protocol
- Type
- string
- Description
ECDSAhoặcEdDSA.
- Name
- operation
- Type
- string
- Description
KEYGEN,SIGN,RESHAREhoặcPING.
- Name
- threshold, participants
- Type
- number, array
- Description
participant_id,party_key,identity_public_keycủa từng thành viên.
- Name
- key_id
- Type
- string
- Description
Khóa ví.
- Name
- signing_input
- Type
- string (base64)
- Description
Chỉ với
SIGN. Chính xác dữ liệu byte cần ký.
- Name
- signing_input_sha256
- Type
- string (hex)
- Description
Chỉ với
SIGN. SHA-256 củasigning_input.
- Name
- signing_context
- Type
- object
- Description
Chỉ với
SIGN, tùy chọn. Chi tiết giao dịch từ Fystack, tối đa 64 KiB.
- Name
- derivation
- Type
- object
- Description
Chỉ với
SIGN, tùy chọn.pathvàdeltaECDSA không hardened.
- Name
- new_threshold, new_participants
- Type
- number, array
- Description
Chỉ với
RESHARE.
Decoded SIGN request
{
"request_id": "3f1c2a8e-7b0d-4d0c-9a51-2f6c4f0e9b12",
"operation": "SIGN",
"protocol": "ECDSA",
"key_id": "8b0f3c4e-...",
"signing_input": "q83vEjRWeJA...",
"signing_input_sha256": "5d41402abc4b2a76...",
"signing_context": {
"version": 1,
"apex_request_id": "wd_01J9...",
"reference": { "type": "onchain_tx", "id": "..." },
"requested_at": "2026-10-12T08:30:00Z",
"chain": { "network": "ethereum", "chain_id": "1", "protocol": "ECDSA" },
"wallet": { "wallet_id": "8b0f3c4e-...", "derivation_path": [44, 60, 0, 0, 0] },
"tx": {
"raw": "0x02f8...",
"payload_hash": "0x5d41402a...",
"verified": true,
"verification_mode": "evm_transaction",
"decoded": { "to_address": "0xAbC...", "value": "1000000000000000000" }
}
},
"threshold": 1,
"participants": [ ... ]
}
verification_mode: evm_transaction, solana_message, tron_transaction, bitcoin_transaction hoặc digest_only.
signing_context chỉ là thông tin gợi ý, không phải bằng chứng. Cosigner không đối chiếu nó với signing_input. Hãy tự giải mã tx.raw, tính lại mã băm, so sánh với signing_input, sau đó kiểm tra địa chỉ đích, số tiền và rủi ro. Từ chối những gì bạn không thể xác minh.
Phản hồi
HTTP 200, Content-Type: application/jwt, ký RS256 bằng khóa máy chủ của bạn. Tối đa 64 KiB. Chuyển hướng sẽ không được theo.
- Name
- request_id
- Type
- string
- Description
Phải khớp với yêu cầu.
- Name
- action
- Type
- string
- Description
approvehoặcreject.pongchoPING.
- Name
- reason
- Type
- string
- Description
Bắt buộc khi
reject, tối đa 1024 byte, bỏ qua khiapprove. Được ghi vào nhật ký kiểm toán.
- Name
- exp
- Type
- number
- Required
- required
- Description
Nên đặt ngắn, ví dụ 60 giây.
Response payload
200{
"request_id": "3f1c2a8e-7b0d-4d0c-9a51-2f6c4f0e9b12",
"action": "reject",
"reason": "destination_not_allowlisted",
"iat": 1791794400,
"exp": 1791794460,
"jti": "b7e5..."
}
Kiểm tra hoạt động
Cosigner gửi "operation": "PING" khi khởi động và cứ mỗi 30 giây. Hãy trả lời "action": "pong" mà không cần chạy chính sách.
- Ping khi khởi động thất bại: cosigner dừng lại.
- Các lần ping sau thất bại: trạng thái sức khỏe hiển thị
callback_unavailablevà mọi yêu cầu ký bị từ chối cho đến khi callback phục hồi.
Máy chủ mẫu
cosigner-cli đi kèm một ví dụ Node.js trong examples/node-callback-server (express + jose).
server.js
app.post('/cosigner/approve', express.text({ type: 'application/jwt' }), async (req, res) => {
try {
// RS256 only, signed by the pinned cosigner key, with exp and request_id.
const request = await verifier.verify(req.body.trim())
if (request.operation === 'PING') {
return res.type('application/jwt').send(await signer.sign(request, { action: 'pong' }))
}
let decision = { action: 'approve' }
if (request.operation === 'SIGN' && !(await policyAllows(request))) {
decision = { action: 'reject', reason: 'destination_not_allowlisted' }
}
res.type('application/jwt').send(await signer.sign(request, decision))
} catch (err) {
res.sendStatus(401)
}
})
Ở trạng thái mặc định, ví dụ này phê duyệt mọi thứ. Hãy thay thế logic quyết định trước khi dùng tiền thật.
Danh sách kiểm tra cho production
- Giữ
require_callback: true.cosigner-cli callback removesẽ tắt hoàn toàn cơ chế phê duyệt. - Mặc định từ chối.
- Giải mã
tx.rawvà đối chiếu vớisigning_input. - Áp dụng danh sách cho phép, hạn mức và kiểm tra AML.
- Đảm bảo idempotent theo
request_id; các lần thử lại sẽ gửi lại cùng token. - Lưu khóa máy chủ trong KMS hoặc HSM. AWS KMS
RSA_4096vớiRSASSA_PKCS1_V1_5_SHA_256hoạt động với RS256. - Phản hồi sớm, trong giới hạn thời gian chờ.
- Thiết lập cảnh báo khi có
callback_unavailablevà khi có yêu cầu bị từ chối.