In the previous chapter, Installation Origin Detection, we learned how the installer acts like a detective to figure out if it has permission to update itself.
Once we know we are allowed to update, we face the next challenge: Getting the files.
Imagine you are running a logistics company.
Your receiving department (the installer) shouldn't care how the package arrives. It just needs to know: "Is the box here, and is it safe to open?"
In our software:
Dual-Source Artifact Retrieval is the abstraction layer that handles this routing automatically.
We want a single command that works regardless of who the user is.
// The goal: One function call
await downloadVersion('1.0.5', '/staging/folder');
The installer acts as a "Universal Receiver." It checks the user type, selects the correct carrier (Artifactory or GCS), handles the paperwork (Manifests), and verifies the security seal (Checksums).
This logic lives in download.ts. It acts as the traffic cop.
// download.ts (Simplified)
export async function downloadVersion(version, path) {
// Check the environment variable
if (process.env.USER_TYPE === 'ant') {
// 1. The Bike Courier Route
return await downloadVersionFromArtifactory(version, path);
}
// 2. The Cargo Ship Route (Default)
return await downloadVersionFromBinaryRepo(version, path);
}
Explanation:
USER_TYPE.For public users, we download raw binary files. However, downloading a file over the open internet is risky. Files can get corrupted, or a malicious actor could tamper with them.
To solve this, we use a Manifest and a Checksum.
Before we download the heavy binary, we download a small text file called a manifest.json. It contains a list of files and their "Fingerprints" (Checksums).
A checksum is a string of characters generated from the file's contents. If a single bit in the file changes, the checksum changes completely.
Here is how the secure download process works:
In download.ts, we stream the download and calculate the hash on the fly.
// download.ts (Simplified Logic)
async function downloadAndVerifyBinary(url, expectedHash, destPath) {
// 1. Start download
const response = await axios.get(url, { responseType: 'arraybuffer' });
// 2. Calculate actual hash of what we received
const hash = createHash('sha256').update(response.data).digest('hex');
// 3. Verify the seal
if (hash !== expectedHash) {
throw new Error(`Security Alert! Expected ${expectedHash}, got ${hash}`);
}
// 4. Safe to write to disk
await writeFile(destPath, response.data);
}
For internal users, we leverage NPM (Node Package Manager).
Why use NPM?
Internal systems often have complex authentication (passwords, tokens, VPNs). npm is already configured on developer machines to handle this authentication. Instead of rewriting complex auth logic, we just ask npm to fetch the file for us.
To use NPM to download a specific version of our binary, we create a temporary "fake" project in our staging folder.
package.json: We create a file saying "I depend on Claude version X".npm ci (Clean Install). NPM handles the login, download, and integrity checks for us.// download.ts (Concept)
async function downloadVersionFromArtifactory(version, stagingPath) {
// 1. Create a dummy package.json
const dummyProject = {
dependencies: {
'claude-native': version
}
};
// 2. Write it to the staging folder
await writeFile(join(stagingPath, 'package.json'), JSON.stringify(dummyProject));
// 3. Let NPM do the heavy lifting
// This handles Auth and Checksums automatically!
await exec('npm ci', { cwd: stagingPath });
}
Whether using a courier or a ship, sometimes things get stuck. The internet connection might drop, leaving the download hanging at 99% forever.
To prevent the installer from freezing specifically during the download, we implement a Stall Timeout.
This acts like a "Dead Man's Switch." We start a timer for 60 seconds. Every time we receive a chunk of data, we reset the timer. If we receive nothing for 60 seconds, the timer fires and kills the connection so we can try again.
// download.ts (Stall Logic)
const controller = new AbortController();
let stallTimer;
// Function to reset the bomb
const resetTimer = () => {
clearTimeout(stallTimer);
// If no data for 60s, abort request
stallTimer = setTimeout(() => controller.abort(), 60000);
}
axios.get(url, {
signal: controller.signal,
onDownloadProgress: () => resetTimer() // Every byte received resets the clock
});
Why this matters: Without this, a user on flaky cafe Wi-Fi might stare at a spinning cursor for hours. With this, we fail fast and retry.
We have built a robust "Universal Receiver."
At this point in the pipeline, we have successfully downloaded the new version, verified it is safe, and placed it in the "Staging Area" (as discussed in Chapter 1).
But the file is still just sitting in a temporary folder. How do we make the computer actually use this new file instead of the old one?
Next Chapter: Symlink-Based Activation
Generated by Code IQ