====== Patch Envelopes ====== A Patch Envelope is a bundle of files, configuration, scripts, or binary data that ZEDEDA Cloud can deliver to a running application instance **without rebooting the VM or recreating the image**. It is the mechanism for updating config, rotating certificates, pushing new scripts, or changing application parameters at runtime across a fleet of edge nodes. Think of it as a live config injection channel -- you change the envelope in ZEDEDA Cloud, the app picks it up at its next poll without any restart or downtime. ===== What a Patch Envelope Actually Is ===== A patch envelope is **not a volume mount** and **not a filesystem change pushed by the controller**. It is a collection of base64-encoded binary blobs (artifacts) that EVE-OS exposes to the app through a local **metadata server** running at the link-local address ''169.254.169.254''. The app is responsible for fetching its own artifacts by calling the metadata server API. This is an important distinction: * The controller delivers the envelope metadata to EVE-OS at the next heartbeat * EVE-OS makes the artifacts available via the metadata server HTTP endpoint * The **app itself** must call the metadata server to discover and download its artifacts * Nothing is written to the app's filesystem automatically -- the app owns the pull and placement logic This design means the app must be written (or configured) to poll the metadata server. A vanilla Ubuntu VM with no custom startup script will not automatically receive patch envelope files. ===== Constraints ===== * Maximum envelope size: **5 MB** total across all artifacts * An app instance can have **only one envelope attached at a time** * An envelope can be attached to **many app instances** (one-to-many) * An envelope belongs to **one project only** * The app must be connected to a **local network instance** -- the metadata server is only reachable from within a local (switch) network instance, not from a direct (passthrough) NIC * Encrypted artifacts are supported (introduced in EVE-OS 14.5 LTS) for secure passing of credentials and secrets ===== Artifact Types ===== ^ Type ^ How it works ^ Best for ^ | **Inline** | Data pasted directly into ZEDEDA Cloud as base64. Stored in the ZEDEDA controller. | Small config files, environment variables, short scripts, certificates under 5 MB | | **External (Binary Artifact)** | A binary artifact already uploaded to a datastore, referenced by the envelope. | Larger files, pre-compiled binaries, firmware blobs | Both types can coexist in the same envelope. ===== How the App Receives Patch Envelope Data ===== EVE-OS runs a metadata server reachable at ''169.254.169.254'' from inside any app connected to a local network instance. Patch envelope endpoints: **Step 1 -- Discover available envelopes:** curl http://169.254.169.254/eve/v1/patch/description.json Response: [ { "PatchId": "699fbdb2-e455-448f-84f5-68e547ec1305", "Version": "1", "BinaryBlobs": [ { "file-name": "app.conf", "file-sha": "a3f1...", "file-meta-data": "YXJ0aWZhY3QgbWV0YWRhdGE=", "url": "http://169.254.169.254/eve/v1/patch/download/699fbdb2.../app.conf" }, { "file-name": "update.sh", "file-sha": "c9b2...", "file-meta-data": "YXJ0aWZhY3QgbWV0YWRhdGE=", "url": "http://169.254.169.254/eve/v1/patch/download/699fbdb2.../update.sh" } ] } ] **Step 2 -- Download a specific artifact:** curl http://169.254.169.254/eve/v1/patch/download/699fbdb2-e455-448f-84f5-68e547ec1305/app.conf \ -o /etc/myapp/app.conf The app checks ''file-sha'' against the downloaded file for integrity verification. The ''file-meta-data'' field is a base64-encoded string of any custom metadata you attached to the artifact -- useful for versioning or targeting logic. **Step 3 -- Act on the file:** The app applies the config, runs the script, installs the certificate, or does whatever makes sense for the workload. This is entirely app-side logic. ==== Typical App-Side Pattern ==== A well-designed edge app that supports patch envelopes typically runs a polling loop: #!/bin/sh METADATA_URL="http://169.254.169.254/eve/v1/patch/description.json" KNOWN_VERSION="" while true; do ENVELOPE=$(curl -sf $METADATA_URL) VERSION=$(echo $ENVELOPE | jq -r '.[0].Version') if [ "$VERSION" != "$KNOWN_VERSION" ]; then echo "New patch envelope version: $VERSION" FILE_URL=$(echo $ENVELOPE | jq -r '.[0].BinaryBlobs[0].url') curl -sf $FILE_URL -o /etc/myapp/app.conf # reload config, signal the process, etc. kill -HUP $(cat /var/run/myapp.pid) KNOWN_VERSION=$VERSION fi sleep 30 done This pattern: poll description.json every 30 seconds, compare the version, download and apply if changed, signal the app to reload. No restart, no downtime. ===== Terraform Examples ===== ==== Creating a Patch Envelope (inline artifact) ==== An envelope with a config file delivered as an inline base64 artifact: resource "zedcloud_patch_envelope" "app_config_pe" { name = "myapp-config-v1" title = "MyApp Config Patch Envelope v1" description = "Runtime config for myapp -- updated without restart" project_id = zedcloud_project.demo_project.id activate = true artifacts { artifact_name = "app.conf" binary_blob = base64encode(file("${path.module}/files/app.conf")) file_meta_data = base64encode("version=1;type=config") } } Notes: * ''activate = true'' makes the envelope live immediately on apply * ''binary_blob'' must be base64 encoded -- Terraform's ''base64encode()'' function handles this * ''file_meta_data'' is optional custom metadata, also base64 encoded * ''artifact_name'' becomes the filename in the metadata server response ==== Multiple Artifacts in One Envelope ==== An envelope with both a config file and a shell script: resource "zedcloud_patch_envelope" "myapp_full_pe" { name = "myapp-full-patch-v2" title = "MyApp Full Patch v2" project_id = zedcloud_project.demo_project.id activate = true artifacts { artifact_name = "app.conf" binary_blob = base64encode(file("${path.module}/files/app.conf")) file_meta_data = base64encode("type=config") } artifacts { artifact_name = "update.sh" binary_blob = base64encode(file("${path.module}/files/update.sh")) file_meta_data = base64encode("type=script") } } ==== Attaching the Envelope to an App Bundle ==== The patch envelope is referenced in the app bundle definition, not the app instance. All instances of that bundle automatically inherit the envelope. resource "zedcloud_application" "myapp" { name = "myapp" title = "MyApp" project_id = zedcloud_project.demo_project.id drives { image_id = zedcloud_image.demo_atl_ub_image_1.id maxsize = 20480 target = "Disk" drvtype = "HDD" } resources { name_of_virtual_machine = "myapp" cpus = 2 memory = 2048 } interfaces { netadapter = "eth0" network_id = zedcloud_network_instance.local_net.id default_net_instance = true } # Attach the patch envelope patch_envelope_id = zedcloud_patch_envelope.myapp_full_pe.id } **The app must be on a local network instance** (''network_id'' pointing to a local switch NI). A direct/passthrough NIC does not expose the metadata server. ==== Updating the Envelope (new version) ==== To push a config change, create a new envelope resource or update the existing one. When ''activate = true'' and you run ''terraform apply'', EVE-OS receives the updated artifact list at the next heartbeat and makes the new version available at the metadata server endpoint. The version number in the description.json response increments, and any app polling the endpoint will detect the change. resource "zedcloud_patch_envelope" "app_config_pe" { name = "myapp-config-v1" title = "MyApp Config Patch Envelope v1" project_id = zedcloud_project.demo_project.id activate = true artifacts { artifact_name = "app.conf" binary_blob = base64encode(file("${path.module}/files/app.conf")) file_meta_data = base64encode("version=2;type=config") } } ===== ZEDUI Walkthrough ===== ==== Create the Envelope ==== - Navigate to **Library** > **Patch Envelopes** - Click the **+** icon - Fill in **Name** (permanent), Title, Description, Project - Set **Activate Patch** to on if you want it live immediately - Click **Next** to the Artifacts section - Click **+** to add an artifact: - **Inline**: paste base64-encoded data directly - **External**: select a Binary Artifact from your Library - Add more artifacts as needed - Click **Add** To get the base64 of a file for inline upload: cat app.conf | base64 ==== Attach to an App Instance ==== - Navigate to **Edge App Instances** - Select the target instance - Click the **...** (More Actions) menu - Click **Attach Patch Envelope** - Select the envelope from the list - Confirm To detach: same path, click **Detach Patch Envelope**. ===== ZCLI ===== # Create a patch envelope with an inline artifact zcli patch-envelope create myapp-config-v1 \ --title="MyApp Config v1" \ --project=demo-project \ --activate=true # Attach to an app instance zcli edge-app-instance attach-patch-envelope MY-APP-INSTANCE \ --patch-envelope=myapp-config-v1 ===== API ===== # Create envelope POST /v1/patchenvelopes { "name": "myapp-config-v1", "title": "MyApp Config v1", "projectId": "", "activate": true, "artifacts": [ { "artifactName": "app.conf", "binaryBlob": "", "fileMetaData": "" } ] } # Attach to an app instance PATCH /v1/appinstances/{app_instance_id} { "patchEnvelopeId": "" } ===== Use Cases ===== ^ Use Case ^ What Goes in the Envelope ^ How the App Uses It ^ | Config file rotation | Updated app.conf or settings.yaml | App polls metadata server, detects version change, writes file, sends SIGHUP | | Certificate rotation | New TLS cert and key PEM files | App detects new cert, writes to /etc/ssl/, reloads web server | | Feature flags | JSON file with feature flag toggles | App reads on startup and on change without restart | | Script execution | Shell script or Python script | App downloads, chmod +x, executes -- handles patching its own dependencies | | Container credentials | Registry auth token (encrypted artifact) | Docker Compose runtime reads credential for next pull | | Edge AI model update | New model weights file (up to 5 MB) | Inference app detects new model, hot-swaps without restarting | ===== Related Resources ===== * [[03_images|Images]] * [[07_edge_apps|Edge Apps]] * [[06_persistent_volumes_and_content_trees|Persistent Volumes and Content Trees]]