Documentation / @etherfold/core / installStreamSeed
Function: installStreamSeed() ​
installStreamSeed<
ABI>(keepStream,locations,options):Promise<StreamSeedInstallOutcome>
Defined in: packages/core/src/stream/seedInstall.ts:370
FETCH a published stream seed from the locations a caller named, CHECK everything a client can establish on its own, and INSTALL it by writing through the public keeper seam.
THE TRUST CONTRACT, which is the thing to read before using this ​
The CALLER names the locations and OWNS that choice (ADR-0066). This fetches from the list it was given, in order, and nowhere else, so there is no origin check to make and no allowlist to build. Where those locations come from is the application's business and the application's risk: a build constant, an environment variable, or a query parameter -- and an app that accepts a RUNTIME OVERRIDE (a ?snapshot= style parameter) has decided to accept a seed from wherever that override points, which nothing here can see or judge. Ordinarily the list comes from the BUILD, and TLS to a named host is what the client relies on.
A content hash is OPTIONAL (expectedContentHash), because only an IMMUTABLE, release-tied artifact can have one pinned: a build cannot know the hash of a ROLLING artifact, which is how this is deployed.
OMISSION is NOT defended against, and that is the uncomfortable half. A seed that simply LEAVES LOGS OUT is structurally perfect: it passes every check below, and detecting it would need the historical logs a public node will not serve. So a compromise of the named host poisons every client that fetches from it, SILENTLY -- and because a stored stream is re-folded by every later generation, the poison is inherited by generations that downloaded nothing. The named host must therefore be trusted the way the BUILD PIPELINE is trusted. The mechanism that would close this is a SIGNATURE against a build-pinned key, which is named and deliberately not built (ADR-0066).
What it CHECKS, all of it before the first write ​
Every mandatory check precedes the first saveNewEvents, which is what makes a half-verified stream unexpressible rather than a state somebody has to define (ADR-0065). In order, and each is a refusal reason:
- Integrity, when a hash was pinned: over the decompressed octets, before the parse.
- Readability: a document this build's reader accepts.
- Identity (ADR-0064): EXACT stream-digest equality, computed by the client from the artifact's own resolved config and stored context. A publisher whose filter is a strict SUPERSET is refused, and the refusal names the DIRECTION.
- Reach-back: the seed covers the block this client reads from.
- Coherence (ADR-0065): O(n) over the events, needing no node.
- Capture depth (ADR-0065): the coverage ends at least
finalityblocks below the head the producer observed.
What is deliberately NOT checked: chain anchoring and bloom consistency (half a defence against half the threat -- a bloom proves FABRICATION, never OMISSION), and omission itself, which is impossible within the premise rather than deferred.
The EMPTINESS gate (ADR-0067) runs before all of them and before any fetch, so none of the checks above is ever reached against a subtree that already holds a stream -- which is why no seed a client downloads can be what costs it its history, and why a client that already has a stream pays no download to be told it may not install over it.
What installing IS ​
A run of ordinary saveNewEvents calls and nothing else (ADR-0063). There is no new keeper operation, no substrate access and no second copy of the segmentation rules: createSegmentedStream already owns which ordinal a segment takes, what the cursor record holds, that the stream's startBlock is the first save's lastFromBlock, and which batches are refused as a hole. All this adds is block arithmetic, and it has three rules -- see writeSeed.
What it REFUSES, and what a refusal costs ​
Refusals come back as DATA (NotInstalledReason), never as a throw, because every one of them is an ordinary condition a host renders. A refusal writes NOTHING and, just as importantly, DELETES nothing: the emptiness probe is built so that refusing an install can never be what destroys the stream it refused (PROBE_FROM_BLOCK).
What DOES throw is a malformed expectedContentHash, the keeper failing mid-install, or the keeper declining a batch that continues exactly what this call itself just wrote. None is an ordinary condition: the first is a mistake in the caller's own source, and the last can only mean something else wrote into this subtree while the install was running, which the one-writer rule forbids.
A RELATIVE, hostless path is a first-class location ​
An artifact shipped inside the application's own build, ordinarily listed LAST so the app still starts when its remote is unreachable or gone. It needs no host, no TLS relationship and no trust decision separate from the app's own, because it arrives in the same delivery as the code that reads it.
const outcome = await installStreamSeed(keeper, [
'https://seeds.example/stratagems.seed.json.gz',
'./stratagems.seed.json.gz',
], {source, streamConfig: resolveStreamConfig({finality: 12})});Type Parameters ​
ABI ​
ABI extends Abi
Parameters ​
keepStream ​
ExistingStream<ABI>
locations ​
string | readonly string[]
options ​
Returns ​
Promise<StreamSeedInstallOutcome>