
Cliente TypeScript enxuto + Python zero-dep e receitas para controlar ações de alto risco atrás de uma aprovação de chave de acesso vinculada ao payload.
Cliente TypeScript fino, de dependência zero, para Cosignet — aprovação humano-no-circuito para ações de alto risco de agentes de IA, com assinaturas de chave de acesso vinculadas ao payload.
Coloque um humano no circuito antes de uma ação perigosa ser executada: pause-a, obtenha uma
aprovação explícita por chave de acesso de uma pessoa (Face ID / Touch ID / Windows Hello / chave
de segurança) e continue apenas com uma decisão assinada vinculada ao payload exato —
altere a ação depois e a assinatura não corresponderá mais. Cosignet é uma
camada de aprovação e evidência, não um executor ou motor de política. Funciona em qualquer lugar com
fetch global + Web Crypto: Node 18+, Cloudflare Workers, Deno e navegadores.
Status: acesso antecipado. Publicado no npm como
@cosignet/sdk.
npm install @cosignet/sdk
import { Cosignet } from '@cosignet/sdk';
const cosignet = new Cosignet({ apiKey: process.env.COSIGNET_API_KEY! });
const decision = await cosignet.requestApproval(
{
username: 'alex',
action: 'Transferência bancária para fornecedor',
payload: { to: 'acct_8821', amount_usd: 4200, memo: 'INV-2025-118' },
notify: 'telegram_or_email',
},
{ onCreated: (c) => console.log('Aprovar aqui:', c.url) },
);
if (decision.status === 'approved') {
// prossiga — decision.rawAssertion é a prova assinada
} else {
// 'rejected' | 'expired' | 'pending' (tempo esgotado)
}
requestApproval faz long-polling sobre sua própria conexão de saída (~saltos de 25s), então funciona
a partir de ferramentas CLI e VPCs restritas atrás de NAT/firewalls — sem necessidade de webhook de entrada,
porta aberta ou IP público.
Se seu sistema não puder obter uma aprovação assinada, a ação bloqueada não deve ser executada. A indisponibilidade nunca deve degradar para auto-aprovação. Trate todo tempo limite, erro de rede e 5xx como "não aprovado".
Notas de implementação:
Consulte Quando o Cosignet está indisponível para orientações completas sobre disponibilidade e break-glass.
const created = await cosignet.createConfirmation({ username, action, payload });
const status = await cosignet.getConfirmation(created.id, { wait: 25 }); // long-poll
Passe idempotencyKey (enviada como cabeçalho Idempotency-Key) para que uma nova tentativa de criação —
por exemplo, após uma interrupção de rede durante um long-poll — com a mesma chave e mesma ação/payload
retorne a confirmação original (idempotent: true) em vez de criar uma duplicata ou re-notificar o aprovador.
Reutilizar uma chave com parâmetros diferentes é rejeitado com um 422.
await cosignet.createConfirmation({
username: 'alex',
action: 'Transferência bancária para fornecedor',
payload: { to: 'acct_8821', amount_usd: 4200 },
idempotencyKey: 'wire-INV-2025-118', // estável por operação lógica
});
Passe returnUrl para mostrar um botão "Retornar a <host>" na página de aprovação
assim que a solicitação for resolvida (aprovada / rejeitada / expirada) — útil para que um humano
possa voltar ao seu aplicativo para re-triggerar uma ação que expirou. Deve ser uma URL https
e é apenas renderizada como um link clicável (nunca buscada no lado do servidor).
await cosignet.requestApproval({
username: 'alex',
action: 'Transferência bancária para fornecedor',
payload: { to: 'acct_8821', amount_usd: 4200 },
returnUrl: 'https://app.example.com/approvals',
});
notify ('none' | 'telegram' | 'email' | 'telegram_or_email') controla o
ping de alcance pessoal para o signatário específico:
telegram — uma DM para o Telegram vinculado do aprovador (telegram_or_email prefere
isso quando o aprovador vinculou um chat).email — um e-mail para o endereço próprio do aprovador. Prioridade: o e-mail direto
que você definiu para o aprovador no painel (apenas entrega), depois o e-mail verificado do membro,
depois o endereço de contato da conta como último recurso.telegram_or_email — Telegram se vinculado, caso contrário o e-mail acima.none — nenhum ping pessoal.Separadamente, o alcance de equipe é uma transmissão Slack para um canal compartilhado, configurado
por conta no painel. É aditivo e sempre dispara quando configurado, independente
de notify (então notify:'none' ainda publica no Slack). Os aprovadores recebem
seus Telegram e e-mail definidos na seção Aprovadores do painel.
Nota sobre a revelação
public: true:o e-mail comprometido no log de transparência é o e-mail verificado designado da conta (escolhido nas configurações do painel, como a parte responsável — não o signatário individual). O e-mail de notificação direta é apenas para entrega e nunca usado para o hash de revelação.
Cada canal é apenas de link: a notificação carrega a URL de aprovação e nunca
a action ou payload. A aprovação sempre requer a chave de acesso vinculada ao payload do aprovador
na página de confirmação — não há aprovação no canal.
verifyWebhookSignature recalcula o HMAC-SHA256 hex do corpo bruto da solicitação
e compara em tempo constante com o cabeçalho Cosignet-Signature. Passe os
bytes exatos que você recebeu — re-serializar JSON altera a assinatura.
import { verifyWebhookSignature } from '@cosignet/sdk';
const ok = await verifyWebhookSignature({
body: rawBody, // string bruta, não JSON re-parseado
signature: req.headers['cosignet-signature'],
secret: process.env.COSIGNET_WEBHOOK_SECRET!,
timestamp: req.headers['cosignet-timestamp'], // proteção opcional contra replay
toleranceSeconds: 300, // opcional
});
if (!ok) return res.status(401).end();
Consulte docs/verification.md para a lista de verificação do lado do chamador: prossiga apenas em approved, compare a decisão com a operação que você está prestes a executar, e trate rejeitado/expirado/tempo limite como paradas fortes.
new Cosignet({ apiKey, baseUrl?, fetch? })createConfirmation(input) → CreatedConfirmationgetConfirmation(id, { wait? }) → ConfirmationrequestApproval(input, { timeoutMs?, onCreated? }) → ConfirmationverifyWebhookSignature({ body, signature, secret, timestamp?, toleranceSeconds? }) → Promise<boolean>Por padrão, o log de transparência retém o payload e não expõe nenhuma identidade de aprovador.
Defina public: true em createConfirmation/requestApproval para optar por uma
aprovação única em revelação pública: sua action bruta + payload tornam-se
legíveis no pacote de verificação, e um hash PBKDF2 do e-mail verificado designado da
conta (escolhido nas configurações do painel – a parte responsável, não o signatário individual) é
comprometido na folha Merkle para que qualquer um possa verificar um endereço candidato contra ele.
await cosignet.requestApproval({
username: 'alice',
action: 'Publicar resolução do conselho Q3',
payload: { docId: 'res-2026-Q3' },
public: true, // permanente, irreversível — veja as ressalvas abaixo
});
examples/ — exemplos prontos para copiar e colar em cURL, Node, Python, GitLab CI, MCP/wrapper de ferramenta e Worker.security/ — vinculação de payload, modelo de prova WebAuthn, modelo de ameaça, log de transparência e divulgação responsável.Bloqueie ações de alto risco atrás de uma aprovação por chave de acesso:
recipes/ai-agent-gate — pause um agente de IA de codificação (Claude Code) antes de executar um comando shell perigoso até que um humano co-assine.recipes/cli-approval — envolva qualquer comando (ex.: terraform destroy).recipes/ci-cd — bloqueie um pipeline (GitHub Actions / GitLab CI) até aprovação.recipes/backend-service — pause uma operação irreversível em um serviço.[email protected]; não abra issues públicas para vulnerabilidades.[email protected].MIT © Cosignet
| Sinal | Significado | Sua ação |
|---|
status: approved (assinatura verifica) | Humano aprovou este payload exato | Prosseguir |
status: rejected | Humano rejeitou | Não executar; reportar ao solicitante |
status: expired | Ninguém decidiu a tempo | Não executar; re-solicitar se ainda necessário |
| Tempo limite, erro de rede ou 5xx | Estado desconhecido | Não executar; tentar novamente com backoff; alertar após N falhas |