SDK Python e CLI
O pacote aether365 é o SDK Python oficial e a interface de linha de comandos da API da Aether365. Encapsula https://api.aether365.io, trata da autenticação, extrai o envelope de resposta e repete automaticamente os pedidos em caso de falhas transitórias. Tudo o que pode fazer com curl pode fazê-lo com o SDK ou a CLI.
O SDK e a CLI usam uma chave de API (ak_live_...) e apontam para o endpoint unificado: os pedidos são encaminhados automaticamente para a região de origem do seu tenant. Consulte Autenticação para perceber como funcionam as chaves.
Instalação
O pacote é distribuído internamente (não está no PyPI). Instale-o diretamente a partir do repositório ou de um checkout local.
bash
# From a local checkout of the monorepo
pip install ./packages/cli
# Or with Poetry, from a path
poetry add ./packages/cliÉ necessário Python 3.12 ou mais recente. A instalação do pacote também disponibiliza o comando aether365 no seu PATH.
Configuração
Tanto o SDK como a CLI leem as mesmas variáveis de ambiente:
| Variável | Finalidade | Predefinição |
|---|---|---|
AETHER365_API_KEY | A sua chave de API (ak_live_...). Obrigatória. | - |
AETHER365_API_URL | Substitui o URL base (por exemplo, o endpoint de dev). | https://api.aether365.io |
AETHER365_OUTPUT | Formato de saída da CLI: table ou json. | table |
bash
export AETHER365_API_KEY="ak_live_..."A região é automática: a API resolve o seu tenant a partir da chave e encaminha o pedido para a região correta, pelo que nunca precisa de configurar nem passar uma região.
Início rápido: SDK
python
from aether365 import Aether365Client
with Aether365Client() as client: # reads AETHER365_API_KEY
tenant = client.tenants.me()
print(tenant["name"])
# Trigger a scan and wait for it to finish
scan = client.scans.create("compliance")
for snapshot in client.scans.wait(scan["id"]):
print(snapshot["status"])
# Read the findings
findings = client.scans.results(scan["id"], result="Failed")
print(f"{len(findings)} failed checks")Início rápido: CLI
bash
# Show the authenticated tenant
aether365 tenant me
# Trigger a scan and follow progress until it finishes
aether365 scan run --type compliance --watch
# List scans as JSON
aether365 --output json scan list
# Fail a CI job when a scan has failed findings (exit code 2)
aether365 scan results <SCAN_ID> --fail-on-findingsO que uma chave de API pode e não pode fazer
Uma chave de API está limitada a uma superfície segura, de leitura e operações. Pode ler os seus dados, iniciar e gerir scans, gerar relatórios e gerir os alvos de superfície de ataque. Não pode executar ações de controlo de conta nem de bloqueio do diretório: gestão de chaves de API, faturação, membros da equipa, adição/remoção de ligações, SSO, aplicação de um plano de remediação ou as escritas conditional-access/break-glass do AI Pilot. Essas ações continuam disponíveis apenas para uma sessão iniciada no dashboard. Um pedido a uma rota não permitida devolve 403 AUTH_INSUFFICIENT_SCOPE.
Referência de comandos e métodos
Cada linha mostra a mesma operação de três formas: curl puro, o método do SDK e o comando da CLI.
Tenant e conta
| Operação | curl | SDK | CLI |
|---|---|---|---|
| Obter o perfil do tenant | GET /tenants/me | client.tenants.me() | aether365 tenant me |
| Listar ligações | GET /tenants/me/connections | client.connections.list() | aether365 tenant connections |
| Preferências de notificação | GET /tenants/me/notifications | client.notifications.get() | aether365 tenant notifications |
| Scans agendados | GET /tenants/me/scheduled-scans | client.scheduled_scans.list() | aether365 tenant scheduled-scans |
Scans
| Operação | curl | SDK | CLI |
|---|---|---|---|
| Listar scans | GET /tenants/me/scans | client.scans.list() | aether365 scan list |
| Iniciar um scan | POST /tenants/me/scans | client.scans.create("compliance") | aether365 scan run --type compliance |
| Obter um scan | GET /scans/{id} | client.scans.get(id) | aether365 scan get <id> |
| Ler resultados | GET /scans/{id}/results | client.scans.results(id) | aether365 scan results <id> |
| Cancelar um scan | POST /scans/{id}/cancel | client.scans.cancel(id) | aether365 scan cancel <id> |
| Ocultar um scan | PATCH /scans/{id}/hide | client.scans.hide(id) | aether365 scan hide <id> |
| Voltar a mostrar um scan | PATCH /scans/{id}/unhide | client.scans.unhide(id) | aether365 scan unhide <id> |
| Eliminar um scan | DELETE /scans/{id} | client.scans.delete(id) | aether365 scan delete <id> |
O curl para iniciar um scan:
bash
curl -X POST https://api.aether365.io/tenants/me/scans \
-H "Authorization: Bearer $AETHER365_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scanType": "compliance"}'Relatórios
| Operação | curl | SDK | CLI |
|---|---|---|---|
| Estado do relatório | GET /scans/{id}/report | client.reports.status(id) | aether365 report status <id> |
| Gerar um relatório | POST /scans/{id}/report/generate | client.reports.generate(id) | aether365 report generate <id> |
| Listar relatórios | GET /tenants/me/reports | client.reports.list() | aether365 report list |
| Transferir um relatório | GET /scans/{id}/report (presigned) | client.reports.download(id, path) | aether365 report download <id> -o out.pdf |
O estado de um relatório é um de ready, rendering, orgNameRequired ou locked. Gerar um relatório pode consumir um crédito de relatório (um pagamento único quando o plano não inclui saldo): é o titular da chave que aceita esse custo ao chamar generate.
A listagem de relatórios é paginada por offset: devolva meta["nextCursor"] (um offset de linha inteiro, ou None na última página) como cursor. client.reports.iter_all() segue o cursor por si.
Superfície de ataque
| Operação | curl | SDK | CLI |
|---|---|---|---|
| Visão geral | GET /tenants/me/attack-surface | client.attack_surface.summary() | aether365 eas summary |
| Histórico | GET /tenants/me/attack-surface/history | client.attack_surface.history() | aether365 eas history |
| Listar alvos | GET /tenants/me/attack-surface/targets | client.attack_surface.targets() | aether365 eas targets list |
| Adicionar um alvo | POST /tenants/me/attack-surface/targets | client.attack_surface.add_target(host) | aether365 eas targets add <host> |
| Atualizar um alvo | PATCH /tenants/me/attack-surface/targets/{id} | client.attack_surface.update_target(id, ...) | aether365 eas targets update <id> --label ... |
| Eliminar um alvo | DELETE /tenants/me/attack-surface/targets/{id} | client.attack_surface.remove_target(id) | aether365 eas targets remove <id> |
| Verificar um alvo | POST /tenants/me/attack-surface/targets/{id}/verify | client.attack_surface.verify_target(id) | aether365 eas targets verify <id> |
| Executar um scan EAS | POST /tenants/me/attack-surface/scan | client.attack_surface.scan() | aether365 eas scan |
Postura, remediação e AI Pilot
| Operação | curl | SDK | CLI |
|---|---|---|---|
| Vista de ameaças / riscos | GET /tenants/me/threats | client.threats.list() | aether365 threats |
| Postura de políticas | GET /tenants/me/policies | client.policies.list() | aether365 policies |
| Capacidades de remediação | GET /tenants/me/remediation/capabilities | client.remediation.capabilities() | aether365 remediation capabilities |
| Listar planos de remediação | GET /tenants/me/remediation-plans | client.remediation.plans() | aether365 remediation plans |
| Obter um plano de remediação | GET /tenants/me/remediation-plans/{id} | client.remediation.plan(id) | aether365 remediation plan <id> |
| Remediáveis do AI Pilot | GET /tenants/me/ai-pilot/remediable | client.ai_pilot.remediable() | aether365 ai-pilot remediable |
| Break-glass do AI Pilot | GET /tenants/me/ai-pilot/break-glass | client.ai_pilot.break_glass() | aether365 ai-pilot break-glass |
| Políticas CA do AI Pilot | GET /tenants/me/ai-pilot/conditional-access | client.ai_pilot.conditional_access() | aether365 ai-pilot conditional-access |
A remediação é apenas de leitura para chaves de API. A aplicação de um plano não está exposta: os itens do action-registry de um plano podem incluir patches de Conditional-Access / authorization-policy capazes de deixar um tenant bloqueado fora do próprio diretório (AADSTS50097), pelo que a aplicação fica no dashboard com um operador a acompanhar. As vistas conditional-access e break-glass do AI Pilot são igualmente expostas apenas em leitura pelo mesmo motivo.
Tratamento de erros e novas tentativas
Cada erro da API corresponde a uma exceção tipada, subclasse de Aether365Error:
python
from aether365 import Aether365Client
from aether365.exceptions import PermissionError_, PlanLimitError, NotFoundError
with Aether365Client() as client:
try:
client.scans.create("compliance")
except PlanLimitError:
print("Scan quota reached for this plan.")
except PermissionError_:
print("Route not allowed for this key, or plan lacks API access.")
except NotFoundError:
print("Resource not found.")O cliente repete automaticamente 429 RATE_LIMITED e 503 SERVICE_STARTING (o arranque a frio da base de dados em dev) com backoff exponencial, respeitando o cabeçalho Retry-After. Os erros de quota como SCAN_PLAN_LIMIT_REACHED são lançados de imediato: não são repetidos.
Usar a CLI em CI
aether365 scan results <id> --fail-on-findings termina com o código 2 quando existe algum resultado falhado, pelo que um pipeline pode condicionar a sua conclusão aos resultados do scan:
bash
aether365 scan run --type compliance --watch
aether365 scan results "$SCAN_ID" --fail-on-findingsA CLI escreve os erros no stderr e termina com 1 em qualquer erro da API e com 2 na barreira de findings.