====== EVE-OS Patch Envelopes ====== ===== What is a patch envelope? ===== A **patch envelope** is EVE-OS's mechanism for delivering/updating data to a running edge app instance **at runtime — without rebooting the VM, recreating the image, or redeploying the app**. It is a container of one or more **binary artifacts (blobs)**: base64-encoded objects that can be anything — a config file, a YAML, certs, a license key, a small binary, etc. ===== What is it used for? ===== The core use case is **decoupling mutable data/config from the immutable app image**. Instead of baking config into the image (and re-rolling the image every time something changes), you attach a patch envelope to the app instance and update //just// that data. Typical scenarios: * Making updated configuration available to a running app without downtime (the app pulls it from the metadata service — nothing is pushed to the guest) * Rotating credentials, certs, tokens, license keys * Delivering site-specific or device-specific parameters to a generic image * Any case where "recreating the image doesn't make sense" or downtime is unacceptable You manage it from the controller (the ''PatchEnvelopeConfiguration'' API), and EVE exposes it to the app locally. ===== What can the binary artifact be? ===== You attach one or more **binary artifacts** to a patch envelope and present them to the app instance; the app then pulls them from the metadata service. So what can that artifact actually be? **Anything — it is an opaque blob to EVE.** EVE does not parse, validate, or execute the artifact; it only delivers the bytes into the app's reach. So a binary artifact can be: * an ''.exe'', a shell script, a Python file * a config file / YAML / JSON / ''.env'' * certs, keys, a license file, a token * a firmware image, a tarball / zip, a small dataset, a DB seed * a file with **any** extension (''.ext'', ''.bin'', ''.dat'', …) — the extension is meaningless to EVE * essentially any file you would otherwise have to bake into the image **EVE never runs it.** For an ''.exe'', EVE transfers the bytes; the **guest app** (e.g. a Windows VM) is what pulls it and executes it. EVE is agnostic about what the blob is or which OS consumes it. ==== Can I store the binary in my datastore and just consume it via the patch envelope? ==== **Yes — that is the //external artifact// model** (see [[#inline vs. external artifacts]] below), and it is the right choice for anything larger than the inline limit (e.g. an ''.exe''). * Put the binary in a **datastore**, reference it from the patch envelope, and the app consumes it. * The app **still pulls it** via the metadata service (''description.json'' → download URL); external artifacts appear under ''VolumeRefs''. EVE downloads the volume host-side, but the app is what retrieves/uses it — it is **not** auto-injected into the guest. * External artifacts **inherit the datastore's ACLs** — the datastore must be reachable and permissioned from the device, same as image datastores. > The exact datastore types supported for patch-envelope external artifacts (HTTP/HTTPS, S3, > Azure blob, container registry, …) may be narrower than the full image-datastore list — > verify against the ''PatchEnvelopeConfiguration'' API for your zedcloud version. ===== Does the app need to constantly poll http://169.254.169.254/... ? ===== The patch envelope metadata service is **pull-only**. There is **no push, no notification, no interrupt** to the guest. So if the app wants to //discover// that a new config exists, it **has to poll** ''description.json''. There is no way around that. Two different things are worth separating: * **"Constant polling"** in the sense of a tight ''curl'' loop hammering the endpoint many times a second — **not** needed. * **"Polling at all"** — this **is** required if you want the app to notice a change on its own. EVE will not tell it. So the accurate statement is: **yes, the app must poll** — you just choose the interval (every 30 s, every 5 min, whatever your freshness tolerance is), and you compare **SHA hashes** to avoid re-applying unchanged blobs. It is still polling; it is just cheap polling. The only way to avoid polling entirely is to **not have the app auto-discover updates** — i.e. only fetch on startup, and force a re-fetch by some external action (redeploy/restart the instance, or bump the app so its entrypoint runs again). That is not the app "noticing" a new config — that is you pushing a lifecycle event. ==== Bottom line for your design ==== * Want the running app to pick up config changes on its own → **it must poll** (''description.json'', SHA-compare, re-download on change). Pick a sane interval. * Don't want polling → fetch once at boot only, and treat any config change as "restart/redeploy the instance." There is **no server-push option** in the current patch-envelope design. ==== Metadata endpoints ==== ^ Endpoint ^ Purpose ^ | ''GET http://169.254.169.254/eve/v1/patch/description.json'' | Lists patch envelopes available to //this// app instance — PatchId, blob filenames, SHA hashes, metadata, download URLs, and VolumeRefs | | ''GET http://169.254.169.254/eve/v1/patch/download/{PatchID}'' | Download **all** artifacts as a single ZIP | | ''GET http://169.254.169.254/eve/v1/patch/download/{PatchID}/{filename}'' | Download **one** artifact by name | > Note: the metadata server is only reachable by app instances attached to a **local** network > instance (the same ''169.254.169.254'' link-local service used for cloud-init/ECO metadata). > An app on a pure switch NI with no local-NI path will not see it. ==== ⚠ Gotcha — reachability of 169.254.169.254 ==== The metadata service **lives inside EVE** on the edge node — it is **not** exposed outside the node. This has direct consequences for how the workload is networked: * **Local-type NI can reach it; Switch-type cannot.** A **Local** network instance routes to ''169.254.169.254''. A **Switch** NI is pure **layer 2** (a bridge to the physical LAN) and has no path to an address that only exists inside EVE. So the app **must** have an interface on a **Local** NI to hit the metadata service. * **Multi-NIC default-route trap.** If a workload is attached to **two** network instances (e.g. one Local + one Switch, or two with default routes) and **both provide a default gateway**, the guest may pick the **Switch** interface as its default route — and then traffic to ''169.254.169.254'' goes out the wrong interface and **fails**. * **Fix — configure it on the Local network instance, NOT inside the guest.** EVE hands routes to the app through the **Local NI configuration** (advertised to the attached app via the NI's DHCP), not via manual ''ip route'' commands. Add a **static route** for the metadata address on the Local NI so the app always routes metadata traffic via that interface regardless of which one holds the default route. In the ''zedcloud_network_instance'' resource this is the ''static_routes'' block: static_routes { prefix = "169.254.169.254/32" gateway = "" } (The NI also has ''propagate_connected_routes'' for auto-propagating its connected routes.) **How to detect changes without busy-polling:** fetch ''description.json'' and compare the **SHA hashes** of the blobs to what you already applied. If unchanged, do nothing. A lightweight periodic check (e.g. every few minutes) is enough — no constant querying. ===== Inline vs. external artifacts ===== There are two flavors, which affects //how// the data reaches the device: - **Inline artifacts (≤ 10 KB)** — carried inside the EdgeDevConfig itself and served directly by the metadata server. Good for small configs. - **External artifacts** — represented as **volumes**, pulled by EVE's volume manager from a configured **datastore**, and referenced via ''VolumeRefs'' in ''description.json''. Use this for larger blobs (binaries, ''.exe'', tarballs, datasets). These inherit the **datastore's ACL/access requirements**, so the datastore must be reachable and permissioned correctly. ===== Practical takeaway ===== * In the app's entrypoint: ''curl'' ''description.json'' once, download the artifact(s), write them where the app expects them, then start. * For live updates, add a modest timer (or a reload trigger) that re-fetches ''description.json'', compares SHAs, and re-downloads only on change. **No continuous polling needed.** * The app must be on a **local network instance** to reach ''169.254.169.254''. * EVE **does not auto-mount** the envelope into the guest filesystem — the app is responsible for pulling from the metadata service. For external/volume-backed artifacts EVE downloads the volume on the host side, but your app still discovers/retrieves it via the metadata endpoints. There is always an app-side fetch step; it does not "just appear as a file" without your app (or its init script) doing the pull. ===== Sources ===== * [[https://github.com/lf-edge/eve/blob/master/docs/PATCH-ENVELOPES.md|EVE PATCH-ENVELOPES.md (lf-edge/eve)]] * [[https://wiki.lfedge.org/display/EVE/EVE+metadata+service|EVE metadata service (LF Edge wiki)]] * [[https://eve-os.readthedocs.io/docs/ECO-METADATA/|ECO Metadata — EVE-OS]]