Pular para conteúdo

AUR-406 — Perfil parser worker offline (parser-worker-v1)

Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.

Este documento descreve a entrada parser-worker-v1 publicada no registry canônico de profiles. O card publica somente documentos: nenhum engine é invocado, nenhum worker é implementado, nenhuma gramática é escrita e o runner permanece fail-closed para esta chave até um card de implementação dependente habilitá-la explicitamente.

Artefatos

Artefato Caminho
Schema .board/schemas/parser-worker-profile.schema.json
Profile .board/oci/profiles/parser-worker-v1.json
Lock .board/locks/oci/parser-worker-v1.lock.json
Registry .board/oci/profiles/registry.v1.json

O registry passa a publicar exatamente seis chaves, em ordem canônica crescente: bootstrap-readonly-v1, fake-provider-v1, go-git-offline-v1, go-unit-offline-v1, parser-worker-v1, registry-v1. Nenhuma chave existente muda de digest, schema ou lock.

Imagem pinada

O lock fixa Go e Bash na imagem de bootstrap já presente localmente, por digest imutável:

aurum-bootstrap-go-bash@sha256:503ac356fa6bca4bad56fade87b5479ff371bd446ffb9c7db91f211323c7c73e

A imagem foi escolhida por prova, não por conveniência. Sob --pull=never --network=none --read-only --user=65534:65534 --cap-drop=ALL (mais --security-opt=no-new-privileges e tmpfs limitado) o probe devolve bash 5.2.26, go1.21.13 e uma única interface de rede, lo; git está ausente dessa imagem. O worker de parsing é um processo Go que lê blobs pela entrada padrão e não executa comando Git, então a imagem de bootstrap basta e a imagem golang pinada por go-git-offline-v1 — a única localmente presente com git 2.39.2 — não é necessária aqui.

Somente aurum-bootstrap-go-bash@sha256:<64 hex> é aceito. Uma tag mutável (aurum-bootstrap-go-bash:1.21), um nome sem digest ou outro repositório é rejeitado com mutable-input antes de qualquer resolução de imagem.

Plano declarado

Limites e isolamento:

Chave Valor
network none
user 65534:65534
cap_drop / cap_add ALL / none
mounts / devices / sockets none / none / none
pull never
tmpfs rw,nosuid,nodev
checkout_readonly true
read_only_rootfs true
no_new_privileges true
privileged false
timeout_seconds 60
memory_mb 256
cpu_millis 1000
pids_limit 128
tmpfs_mb 128
stdout_limit_bytes / stderr_limit_bytes 65536 / 65536
max_input_files / max_input_bytes 10000 / 67108864
module_cache /go/pkg/mod (module_cache_read_only: true)

Capacidades do worker de parsing, todas verificadas pelo loader:

Chave Valor Efeito
worker digest-pinned o binário do worker só é admitido content-addressed
worker_root /tmp/aurum-parser-worker o worker vive no tmpfs limitado, nunca no checkout
grammar_set digest-pinned o grammar set só é admitido content-addressed
grammar_set_root /tmp/aurum-parser-grammars as gramáticas vivem no tmpfs limitado, nunca no checkout
code_execution denied o código analisado é dado, nunca programa: nada dele é executado
host_filesystem denied nenhum caminho do host é legível ou gravável pelo plano
subprocess denied a allowlist de subprocesso é vazia; qualquer subprocesso é negado
blob_source stdin blobs entram apenas pela entrada padrão, nunca por arquivo do host
max_blob_bytes 1048576 blob limitado; 0 é ilimitado e é recusado
max_blobs 256 número de blobs por execução limitado
parse_timeout_seconds 5 wall-clock por blob analisado

worker: "digest-pinned" e grammar_set: "digest-pinned" pinam a exigência, não os bytes: o binário do worker, as gramáticas e qualquer parser de linguagem são não-objetivos deste card e pertencem ao card de implementação dependente. O que este card garante é que um worker ou um grammar set sem digest nunca é resolvido.

Comandos permitidos

["go", "test", "-mod=readonly", "./..."]

Qualquer outro comando, ou o mesmo comando com aridade diferente, invalida o plano. O ambiente é fechado sobre exatamente estas chaves:

Variável Valor
GOPROXY off
GOSUMDB off
GONOSUMDB *
GOTOOLCHAIN local
AURUM_PARSER_WORKER_ROOT /tmp/aurum-parser-worker
AURUM_PARSER_GRAMMAR_ROOT /tmp/aurum-parser-grammars
HTTP_PROXY (vazio)
HTTPS_PROXY (vazio)
NO_PROXY *

AURUM_PARSER_WORKER_ROOT/worker_root e AURUM_PARSER_GRAMMAR_ROOT/grammar_set_root são duas grafias dos mesmos dois diretórios; o teste de integração recusa qualquer divergência entre elas, porque uma divergência é exatamente um filesystem que o plano não limitou. Nenhuma credencial, valor de ambiente do host ou segredo entra no documento, e o loader recusa qualquer valor com forma de credencial mesmo em campo cujo valor seja pinado por constante.

Códigos de rejeição

O loader (ValidateRegistryAUR406 / ValidateProfileAUR406) é side-effect-free, não abre socket, não cria subprocesso e devolve exatamente um código:

Código Causa
registry-missing registry ausente ou não regular
schema-invalid registry, schema ou profile fora da forma canônica (inclui chave duplicada dentro de um objeto JSON e limite obrigatório ausente, como parse_timeout_seconds)
schema-missing documento de schema ausente
schema-digest-mismatch schema_digest não reproduz os bytes do schema
profile-missing documento do profile ausente
profile-unregistered chave desconhecida, ou parser-worker-v1 ausente do registry
duplicate-profile a mesma chave publicada duas vezes
order-invalid chaves fora da ordem canônica crescente
credential-present valor de ambiente com forma de credential real
digest-invalid digest vizinho fora do formato sha256:<64 hex>, ou worker/grammar set sem digest
unsafe-plan limite obrigatório relaxado, blob ilimitado, root de worker/gramática fora do tmpfs, blob vindo do host, execução de código/subprocesso/filesystem do host habilitados, rede, mount, socket, device, root ou capability
lock-missing lock ausente
mutable-input imagem por tag mutável, repositório inesperado ou lock fora do schema
lock-digest-mismatch lock_digest não reproduz os bytes do lock

Mutantes provados

O acceptance executa cada mutante contra bytes reais em disco e exige o código declarado; o restore volta byte-idêntico.

Mutante Mudança Código
MUT-001 chave parser-worker-v1 duplicada com bytes diferentes duplicate-profile
MUT-002 imagem do lock por tag mutável mutable-input
MUT-003 network de none para host unsafe-plan
mount habilitado mounts para bind unsafe-plan
socket habilitado sockets para unix unsafe-plan
device habilitado devices para /dev/fuse unsafe-plan
root user para 0:0 unsafe-plan
capability adicionada cap_add para SYS_ADMIN unsafe-plan
execução de código code_execution para allowed unsafe-plan
filesystem do host host_filesystem para read-only unsafe-plan
subprocesso subprocess para allowed unsafe-plan
gramática fora do tmpfs grammar_set_root para /workspace/.board unsafe-plan
worker fora do tmpfs worker_root para /workspace/internal unsafe-plan
blob do host blob_source para host-file unsafe-plan
blob ilimitado max_blob_bytes para 0 unsafe-plan
grammar set sem digest grammar_set para unpinned digest-invalid
worker sem digest worker para mutable digest-invalid
limite ausente parse_timeout_seconds removido do documento schema-invalid
credential real HTTP_PROXY para uma chave com forma real (montada em runtime) credential-present

O vetor de credential nunca aparece como literal em arquivo versionado: ele é montado em runtime, dentro da cópia descartável do acceptance, para que o portão de credenciais de oci-run não veja forma de segredo em nenhuma entrada materializada nem em nenhuma saída.

Loaders vizinhos

Registrar uma sexta chave fecha a allowlist dos loaders já entregues, então este card os estende no mesmo commit:

  • tests/unit/AUR-402.go — allowlist de chaves do registry canônico.
  • tests/unit/AUR-403.go — allowlist, aridade (6) e ordem posicional.
  • tests/unit/AUR-404.go — conjunto de chaves registradas e switch do loader.
  • tests/unit/AUR-405.go — conjunto de chaves registradas e switch do loader.
  • tests/contracts/AUR-404.go — aridade exata do registry (5 para 6).

Cada extensão é aditiva: nenhuma rejeição anterior desaparece, nenhuma igualdade vira desigualdade e nenhuma aridade exata vira mínimo. Nenhum desses loaders lê os documentos do parser worker: cada um checa apenas os caminhos declarados e a forma do digest, de modo que registrar o parser worker não pode re-apontar silenciosamente o plano Go unit, o plano Git nem o plano do fake provider.

A aridade total do registry é asserida em tests/unit/AUR-406.go, que este card possui. tests/contracts/AUR-406.go não assere aridade: este card é dono da chave parser-worker-v1, não do tamanho do registry, e a próxima carta de profile deve quebrar apenas onde sua posse estiver declarada.

Diagnóstico offline

# a chave está publicada e em ordem?
grep -n '"key"' .board/oci/profiles/registry.v1.json

# os digests do registry reproduzem os bytes?
sha256sum .board/schemas/parser-worker-profile.schema.json \
          .board/locks/oci/parser-worker-v1.lock.json
printf '%s' "$(sed -n 's/.*"image": "\(.*\)".*/\1/p' \
  .board/locks/oci/parser-worker-v1.lock.json)" | sha256sum

# o runtime da imagem pinada ainda satisfaz o plano?
docker run --rm --pull=never --network=none --read-only \
  --user=65534:65534 --cap-drop=ALL --entrypoint= \
  aurum-bootstrap-go-bash@sha256:503ac356fa6bca4bad56fade87b5479ff371bd446ffb9c7db91f211323c7c73e \
  bash -c 'command -v go; ls /sys/class/net'