[bug] atlas blast walks OWNS inbound, so owners land in their property's blast
I maintainer di solito rispondono entro 3 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- bun, sqlite, typescript
Direzione di ricerca
Inizia da ATLAS/Store.ts, leggi OPERATIONAL_EDGE_KINDS e il percorso in entrata in Store.blast(), poi esamina i test esistenti di blast() e owns(). Riproduci i casi dell’ospite e dell’host descritti nell’issue: il blast di un ospite dovrebbe contenere solo il suo vero dipendente, l’host dovrebbe continuare a raggiungere entrambi gli ospiti tramite RUNS_ON e owns(host) dovrebbe continuare a elencare entrambi gli ospiti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
What happened
atlas blast walks OWNS inbound, so owners land in their property's blast
Summary
atlas blast <key> reports assets that do not rely on <key>: whatever owns it, and, when the owner is also a target of other edges, everything else that owner owns. The cause is one line in Store.blast(), which walks OWNS inbound along with the genuinely operational edge kinds.
Measured on 8.761.0 (the release file, byte-identical to the tarball), Ubuntu LXC, Bun 1.3.14. First found and fixed locally on 7.40.4; the upgrade replaced the file and the behaviour returned.
Mechanism
AtlasSystem.md defines the two queries separately:
blast <key>: "inbound operational closure, what relies on "owns <key>: "OWNS closure, what deleting orphans"
ATLAS/Store.ts lists OWNS in OPERATIONAL_EDGE_KINDS, and blast() walks every kind in that list inbound:
SELECT e.src FROM edge e JOIN reach r ON e.dst = r.id
WHERE e.kind IN (${kinds}) AND e.status = 'active'
-- .all(key, ...OPERATIONAL_EDGE_KINDS, key)
For every other kind in the list, src KIND dst means src relies on dst, so walking inbound answers "what relies on dst". src OWNS dst means the reverse: src holds dst. Walking it inbound adds the owner to the blast of anything it owns, and the recursion then continues from the owner through its own inbound edges.
owns() and exposed() walk OWNS outbound in their own queries, so neither depends on it being in blast().
Reproduction
A self-contained script: a temporary Atlas database built through Store.applyRun(), then three blast() calls. Case A uses exactly the edges the stock Cloudflare collector emits for a zone (Cloudflare.ts: zone OWNS dns_record, zone OWNS hostname, worker SERVES hostname). Case B is a collector that pairs OWNS with a reverse operational edge: a hypervisor host OWNS its guests and each guest RUNS_ON the host.
// bun repro-blast.ts <path to ATLAS/Store.ts in the install>
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
const { Store } = await import(resolve(process.argv[2]));
const store = new Store(join(mkdtempSync(join(tmpdir(), "atlas-blast-repro-")), "atlas.db"));
store.applyRun("cloudflare", "full", { complete: true,
assets: [
{ kind: "domain", key: "domain:example.com", name: "example.com" },
{ kind: "domain", key: "domain:api.example.com", name: "api.example.com" },
{ kind: "dns_record", key: "dns:example.com/A/api.example.com", name: "A api.example.com" },
{ kind: "worker", key: "cloudflare:worker:api", name: "api" } ],
edges: [
{ kind: "OWNS", srcKey: "domain:example.com", dstKey: "dns:example.com/A/api.example.com", srcKind: "domain", dstKind: "dns_record" },
{ kind: "OWNS", srcKey: "domain:example.com", dstKey: "domain:api.example.com", srcKind: "domain", dstKind: "domain" },
{ kind: "SERVES", srcKey: "cloudflare:worker:api", dstKey: "domain:api.example.com", srcKind: "worker", dstKind: "domain" } ] });
store.applyRun("hypervisor", "full", { complete: true,
assets: [
{ kind: "machine", key: "machine:host", name: "host" },
{ kind: "machine", key: "machine:guest-a", name: "guest-a" },
{ kind: "machine", key: "machine:guest-b", name: "guest-b" },
{ kind: "service", key: "service:app-on-a", name: "app-on-a" } ],
edges: [
{ kind: "OWNS", srcKey: "machine:host", dstKey: "machine:guest-a", srcKind: "machine", dstKind: "machine" },
{ kind: "OWNS", srcKey: "machine:host", dstKey: "machine:guest-b", srcKind: "machine", dstKind: "machine" },
{ kind: "RUNS_ON", srcKey: "machine:guest-a", dstKey: "machine:host", srcKind: "machine", dstKind: "machine" },
{ kind: "RUNS_ON", srcKey: "machine:guest-b", dstKey: "machine:host", srcKind: "machine", dstKind: "machine" },
{ kind: "RUNS_ON", srcKey: "service:app-on-a", dstKey: "machine:guest-a", srcKind: "service", dstKind: "machine" } ] });
for (const key of ["dns:example.com/A/api.example.com", "domain:api.example.com", "machine:guest-a"])
console.log(`blast ${key}\n -> ${store.blast(key).map((r: any) => r.canonical_key).sort().join(", ") || "(nothing)"}`);
Observed on stock 8.761.0:
blast dns:example.com/A/api.example.com
-> domain:example.com
blast domain:api.example.com
-> cloudflare:worker:api, domain:example.com
blast machine:guest-a
-> machine:guest-b, machine:host, service:app-on-a
Expected, and observed with the fix below:
blast dns:example.com/A/api.example.com
-> (nothing)
blast domain:api.example.com
-> cloudflare:worker:api
blast machine:guest-a
-> service:app-on-a
Impact
It over-reports rather than under-reports, so a delete preview errs on the cautious side. The cost is that the answer to the documented question is wrong, in both places Atlas is meant to be relied on:
- Rotation. AtlasSystem.md gives
atlas blaston a credential asset as the "what breaks when I rotate this" answer. Any owner above the credential's holder lands in that list. - Previews before destructive ops (Algorithm criterion 16). With stock collectors the noise is the owning zone. With a collector that models hosts and guests it is the whole host: here, one container's blast listed the hypervisor and every other container on it, burying the one real dependent.
Suggested fix
Leave OPERATIONAL_EDGE_KINDS as it is (other queries use it) and drop OWNS from the inbound walk in blast() only:
blast(key: string) {
const walkKinds = OPERATIONAL_EDGE_KINDS.filter((k) => k !== "OWNS");
const kinds = walkKinds.map(() => "?").join(",");
// ...same query...
.all(key, ...walkKinds, key) as Array<Record<string, unknown>>;
}
Tests
A three-case test on case B's graph: a guest's blast is exactly its real dependent; the host's blast still reaches both guests and the service through RUNS_ON; owns(host) still lists both guests. Stock: 2 pass, 1 fail. Fixed: 3 pass, other Atlas tests 12 of 12. Happy to open a PR with both.
Details
- Kind: bug
- LifeOS version: 8.761.0
Filed with the LifeOS /feedback command. The reporter approved this exact text before it was sent; paths, secrets and personal details were stripped locally.
- Lingua principale
- TypeScript
- Stelle
- 19.6k
- Fork
- 2.5k
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di danielmiessler/LifeOS
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
danielmiessler/LifeOS#2413 ·
I maintainer di solito rispondono entro 3 giorni
-
Pulse v7: buildPreferencesFromTelos() bypasses telosSectionOrFile(), renders empty on unified-TELOS installsForse già presa @zephyr91 l’ha presa 2 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
danielmiessler/LifeOS#2393 ·
I maintainer di solito rispondono entro 3 giorni
-
WorkSweep re-creates every [Goal] and [BPE] issue when gh issue list failsForse già presa @Bowlerjim l’ha presa 1 giorno fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
danielmiessler/LifeOS#2391 ·
I maintainer di solito rispondono entro 3 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
danielmiessler/LifeOS#2381 ·
I maintainer di solito rispondono entro 3 giorni
-
DetectAI's remediation pointer is dangling: "use a voice/authoring skill" names nothingForse già presa @Bowlerjim l’ha presa 1 giorno fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
danielmiessler/LifeOS#2380 ·
I maintainer di solito rispondono entro 3 giorni
Tutte le issue di danielmiessler/LifeOS
Issue simili
-
[Bug]: Pen taps on sidebar rows start a drag and lose their clickForse già presa @makhnatkin l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
b310-digital/teammapper#1515 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 79/100
puckeditor/puck#1884 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
outline/outline#14378 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
nicobailon/pi-web-access#642 ·
I maintainer di solito rispondono entro 1 giorno