AUR-407 — Perfil SQLite offline (sqlite-offline-v1)
Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.
Este documento descreve a entrada sqlite-offline-v1 publicada no registry
canônico de profiles. O card publica somente documentos: nenhum engine é
invocado, nenhum banco é aberto, nenhum schema de state store é definido 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/sqlite-offline-profile.schema.json |
| Profile | .board/oci/profiles/sqlite-offline-v1.json |
| Lock | .board/locks/oci/sqlite-offline-v1.lock.json |
| Registry | .board/oci/profiles/registry.v1.json |
O registry passa a publicar exatamente sete 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, sqlite-offline-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; sqlite3,
libsqlite3, cc/gcc e git estão ausentes dessa imagem. O mesmo probe
na imagem golang@sha256:4746d264… — a outra imagem local com Go — devolve
git 2.39.2 e libsqlite3.so.0, mas nenhum binário sqlite3 e nenhum
compilador C utilizável sob esse perfil.
Isso é o que determina o plano, e não o contrário: sqlite_driver_cgo: false e
CGO_ENABLED: "0" fixam um driver SQLite puro Go, de modo que o plano nunca
exige o binário sqlite3 nem uma biblioteca nativa. A ausência dessas duas
coisas na imagem pinada não é uma lacuna tolerada — é exatamente o que o
profile nega. Um driver ligado por cgo é rejeitado com unsafe-plan.
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 state store, todas verificadas pelo loader:
| Chave | Valor | Efeito |
|---|---|---|
sqlite_driver |
digest-pinned |
o driver só é admitido content-addressed |
sqlite_driver_cgo |
false |
driver puro Go: nenhuma lib nativa, nenhum sqlite3 |
database_mode |
ephemeral |
o banco existe apenas durante a execução |
database_root |
/tmp/aurum-sqlite-state |
o banco vive no tmpfs limitado, nunca no checkout |
host_database |
denied |
nenhum arquivo de banco do host é aberto |
extension_loading |
denied |
load_extension e qualquer extensão são negados |
remote_uri |
denied |
URI remota (file: com vfs=/host, HTTP) é negada |
implicit_persistence |
denied |
nada sobrevive à execução sem card explícito |
attach_database |
denied |
ATTACH de outro banco é negado |
journal_mode |
memory |
nenhum journal/WAL colateral em disco |
max_database_bytes |
33554432 |
teto exato (32 MiB) dentro do tmpfs de 128 MiB |
max_open_connections |
4 |
conexões simultâneas limitadas |
busy_timeout_ms |
2000 |
espera de lock limitada |
query_timeout_seconds |
5 |
wall-clock por consulta |
sqlite_driver: "digest-pinned" pina a exigência, não os bytes: o driver, o
schema do state store, migrações e consultas são não-objetivos deste card e
pertencem ao card de implementação dependente. O que este card garante é que um
driver sem digest nunca é resolvido.
Os limites são constantes exatas, nunca mínimos: max_database_bytes: 0 e
max_database_bytes: 34359738368 são ambos rejeitados, o primeiro por ser
ilimitado e o segundo por não caber no tmpfs que teria de segurá-lo. O loader
verifica ainda, de forma independente das constantes, que
max_database_bytes <= tmpfs_mb * 1024 * 1024 — um banco que não cabe no próprio
limite teria de transbordar para fora dele.
database_root é validado por expressão ancorada nas duas pontas
(^/tmp/aurum-sqlite-state$), então /workspace/.board e
/tmp/aurum-sqlite-state/../../etc são negados pelo mesmo motivo: nenhum dos
dois é o diretório limitado.
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 |
CGO_ENABLED |
0 |
AURUM_SQLITE_DATABASE_ROOT |
/tmp/aurum-sqlite-state |
SQLITE_TMPDIR |
/tmp/aurum-sqlite-state |
HTTP_PROXY |
(vazio) |
HTTPS_PROXY |
(vazio) |
NO_PROXY |
* |
AURUM_SQLITE_DATABASE_ROOT, SQLITE_TMPDIR e database_root são três grafias
do mesmo diretório; o teste de integração recusa qualquer divergência entre
elas, porque SQLITE_TMPDIR é exatamente onde um arquivo temporário de spill
apareceria se apontasse para fora do tmpfs limitado. 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 (ValidateRegistryAUR407 / ValidateProfileAUR407) é
side-effect-free, não abre socket, não abre banco, 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 query_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 sqlite-offline-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 driver SQLite sem digest |
unsafe-plan |
limite obrigatório relaxado ou excedido, tmpfs ilimitado, banco fora do tmpfs, database host, extension loading, URI remota, persistência implícita ou ATTACH habilitados, driver cgo, journal persistente, 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 |
As constantes de segurança do schema são pinadas duas vezes: nos bytes do
documento de schema e em profileSchemaConstants407 /
environmentSchemaConstants407, dentro de tests/unit/AUR-407.go. Relaxar o
schema e recomputar o schema_digest no registry move os dois arquivos de forma
consistente e ainda assim falha, porque o mapa em Go não se move com eles.
Mutantes provados
O acceptance executa cada mutante contra bytes reais em disco, na cópia descartável, e exige o código declarado; o restore volta byte-idêntico.
| Mutante | Mudança | Código |
|---|---|---|
| MUT-001 | chave sqlite-offline-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 |
| database host | host_database para read-only |
unsafe-plan |
| extension loading | extension_loading para allowed |
unsafe-plan |
| URI remota | remote_uri para allowed |
unsafe-plan |
| persistência implícita | implicit_persistence para allowed |
unsafe-plan |
ATTACH |
attach_database para allowed |
unsafe-plan |
| journal persistente | journal_mode para wal |
unsafe-plan |
| banco fora do tmpfs | database_root para /workspace/.board |
unsafe-plan |
| traversal do root | database_root para /tmp/aurum-sqlite-state/../../etc |
unsafe-plan |
| driver cgo | sqlite_driver_cgo para true |
unsafe-plan |
| banco ilimitado | max_database_bytes para 0 |
unsafe-plan |
| banco gigante | max_database_bytes para 34359738368 |
unsafe-plan |
| tmpfs ilimitado | tmpfs_mb para 0 |
unsafe-plan |
| driver sem digest | sqlite_driver para unpinned |
digest-invalid |
| limite ausente | query_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 sétima 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 (7) e ordem posicional.tests/unit/AUR-404.go— conjunto de chaves registradas eswitchdo loader.tests/unit/AUR-405.go— conjunto de chaves registradas eswitchdo loader.tests/unit/AUR-406.go— conjunto de chaves registradas eswitchdo loader.tests/contracts/AUR-404.go— aridade exata do registry (6para7).
Cada extensão é aditiva: nenhuma rejeição anterior desaparece, nenhuma igualdade vira desigualdade, nenhuma aridade exata vira mínimo e nenhuma chave sai de allowlist. Nenhum desses loaders lê os documentos do state store: cada um checa apenas os caminhos declarados e a forma do digest, de modo que registrar o SQLite não pode re-apontar silenciosamente o plano Go unit, o plano Git, o plano do fake provider nem o plano do parser worker.
A aridade total do registry é asserida em tests/unit/AUR-407.go, que este card
possui. tests/contracts/AUR-407.go não assere aridade: este card é dono da
chave sqlite-offline-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/sqlite-offline-profile.schema.json \
.board/locks/oci/sqlite-offline-v1.lock.json
printf '%s' "$(sed -n 's/.*"image": "\(.*\)".*/\1/p' \
.board/locks/oci/sqlite-offline-v1.lock.json)" | sha256sum
# o runtime da imagem pinada ainda satisfaz o plano puro Go?
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; command -v sqlite3 || echo sqlite3-absent; ls /sys/class/net'