Skip to main content

Deploy Without the Widget

@le-space/core holds the deployment logic itself: what a rootfs manifest must contain, which CRNs can host a profile, how an Aleph INSTANCE is built and broadcast, how a guest is configured after boot, and how relays register themselves for discovery.

It runs in both Node and the browser. Everything above it — the GitHub Action, the CLI, the widget — is a different way of driving these same functions.

Reach for it directly when you are building a fourth way.

Install

npm install @le-space/core

Pin a version rather than a tag. latest and next move independently of each other, so neither is reliably the newer one — see the note in Getting Started.

@le-space/core takes fetch and a signer as arguments rather than reaching for them. That is what lets the same function serve a CI runner holding a private key and a browser holding a MetaMask connection.

What each module owns

ModuleOwns
manifestsvalidateRootfsManifest, verifyRootfsExists — a manifest is checked before anything is signed
crnsfetchCrnsFromList, filterDeployableCrns — candidate discovery and compatibility
instance-deploymentBuilding the INSTANCE message, SSH key normalisation
broadcastPublishing a signed message and waiting for Aleph to process it
deployment-inspectionReading back what a deployment actually became
runtimeWaiting for the CRN to expose runtime networking and mapped ports
guestnotifyCrnAllocation, configuring the guest after boot
bootstrap-registrationPublishing and reading relay bootstrap records
bootstrap-config, bootstrap-reconcileGuest configuration handoff, reconciling owner records
port-forwardingThe port-forward aggregate a profile requires
aggregate-publication, retention, forgetAleph aggregate writes, keeping N deployments, FORGET
crn-controlTalking to a CRN directly
aleph-normalizersTurning Aleph's payloads into the shapes above
constantsChannels, defaults, API hosts

All are re-exported from the package root.

The shape of a deployment

The functions compose in a fixed order, and knowing it makes the module list above readable:

validateRootfsManifest → verifyRootfsExists the image exists and is processed

fetchCrnsFromList → filterDeployableCrns who could host this

build INSTANCE → broadcast → wait processed Aleph accepts it

publish port-forward aggregate the ports the profile declared

wait for runtime networking the CRN actually exposes them

configure guest → publish bootstrap record the relay is usable and findable

Each step can fail in a way the next cannot recover from, which is why they are separate exports rather than one deploy(). A caller that wants CRN failover loops over the middle of this pipeline; see Deployment Lifecycle.

CRN discovery has two sources

fetchCrnsFromList reads crns.json. When that service is down, the same candidates can be read from the corechannel aggregate, which every Aleph API host serves. The aggregate carries no liveness signal, so candidates from it are probed before they cost a deployment attempt.

crns.json stays the default — it is the only source carrying qemu_support and free capacity. The reasoning, and what would change it, is in CRN Discovery.

Bootstrap registration

A relay publishes a signed record so browsers can find it later without hardcoding an address. publishRelayBootstrapRegistration writes it; waitForRelayBootstrapRegistration polls until Aleph serves it back.

The wait matters more than it looks: a record that was accepted but is not yet visible is indistinguishable from one that failed, and a deployment whose registration never became visible is running and paid for but undiscoverable. The full model is in Aleph Bootstrap.

Where it sits in the stack

@le-space/shared-types → @le-space/core → @le-space/browser → @le-space/ui
↘ @le-space/node

@le-space/core is environment-agnostic on purpose. Browser-only concerns (injected wallets) live in @le-space/browser; Node-only ones (keys on disk, Actions plumbing) in @le-space/node. If a change to core needs window or fs, it belongs one layer out — that rule is what keeps the same deployment behaving identically from CI and from a browser.

See Package Boundaries.