Skip to content

feat(install): install from install.socket.dev, optionally without github.com - #145

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
feat/hosted-installer
Open

feat(install): install from install.socket.dev, optionally without github.com#145
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
feat/hosted-installer

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Points the documented one-liner at a Socket-controlled domain, and lets the archives come
from Socket too:

curl -fsSL https://install.socket.dev/patch | sh

The old URL was raw.githubusercontent.com — a third-party CDN serving a script users pipe
into a shell, and the first URL a locked-down egress policy blocks.

Companion PR: SocketDev/depscan#23840, which serves the host. Nothing here breaks before
that lands
: the drift workflow and the Socket-origin CI step are both inert until the
host resolves, and the README's alternative URL works today.

What the host serves

A byte-for-byte copy of scripts/install.sh, with its SHA-256 at /patch.sha256. The
README tells people to diff it, so that has to hold literally. The GitHub raw URL keeps
working and serves the same bytes.

Installing without reaching github.com

New SOCKET_PATCH_BASE_URL points the archive downloads at any releases base answering
GitHub's two asset paths:

curl -fsSL https://install.socket.dev/patch \
  | SOCKET_PATCH_BASE_URL=https://install.socket.dev/SocketDev/socket-patch/releases sh

install.socket.dev relays exactly those paths from the GitHub release, which is why one
template covers both origins and the script needs no branching.

A new release needs no publish for this. The origin resolves "latest" per request
against the upstream release, so cutting 3.4.0 makes it installable from Socket's host
immediately — nothing runs at release time.

socket-patch --update can use the same host today with no CLI changes, via the
SOCKET_UPDATE_BASE_URL override it already has. One caveat documented rather than papered
over: a non-default value intentionally downgrades the downloaded binary's version
self-check from hard-fail to a warning, because that knob targets mirrors that may
repackage. Making Socket's host a first-class endpoint set that keeps the strict check is a
CLI change, not a hosting one — deliberately not here.

Also adds SOCKET_PATCH_INSTALL_DIR, which wins over both existing defaults: what
unprivileged installs into a toolchain-managed prefix need, and what makes the script
testable without writing to a system path.

Trust model: unchanged, and the docs say so

Binaries still come from the GitHub release and are still verified against its
SHA256SUMS. Nothing is signed. Whichever origin serves the bytes, the checksums come from
that same origin. Hosting moved who serves the script, nothing more.

The default origin is still GitHub, on purpose

Flipping it is one line, held until the relay is verified in prod. A script defaulting to a
host that does not answer yet is a broken installer for everyone running it from a git
checkout or the raw URL — and CI's end-to-end install step would fail on main
immediately. Sequence: this merges → depscan#23840 deploys → flip the default.

Four gaps closed around a curl | sh artifact

  1. install.sh was only shellchecked, never run. CI now installs with it end to end
    and execs the result — twice, once with the default origin and once through
    SOCKET_PATCH_BASE_URL, so the URL template is covered too.
  2. A third step installs through install.socket.dev and asserts the installed version
    matches what that host reports as latest. Skips itself with a notice until the host
    resolves.
  3. Nothing kept the URL consistent across the README, the script's usage comment, and
    the runbook; a grep guard fails if any drifts.
  4. Nothing checked the hosted copy. New installer-drift workflow (weekly + dispatch)
    diffs the served bytes against scripts/install.sh, verifies the published checksum, and
    shellchecks what is served. Not part of CI — it tests a deployed artifact, so a red run
    means "bump the submodule pin in depscan", not "this PR is broken". It also names the
    specific failure this design is exposed to: a Cloudflare bot challenge, which would
    otherwise feed an HTML interstitial to sh.

docs/installer-hosting.md is the runbook for the non-obvious part: the hosted copy is
published from depscan's vendored submodule pin, so an installer change here goes live on a
submodule bump plus a deploy.

Verified

CI green on the pre-squash commits — all six Shell steps in lint-ecosystems passed,
including both end-to-end installs, and the Socket-origin step correctly emitted
install.socket.dev/patch/latest does not answer yet — skipping. Locally, against the real
relay: latest and pinned installs both succeeded and were checksum-verified, and
socket-patch --update worked through the same host (--dry-run resolved latest;
--update 3.2.0 downloaded and swapped).

The only failing check is hosted-e2e / gem_bundler_hosted_redirect_and_known_install_defect
— the known gem/Bundler marshal data too short defect against rubygems.org, unrelated to
this change.

Sequencing note

depscan's submodule pin is 19 commits behind main, and scripts/install.sh does
differ (main has the SC2144 detect_libc fix). So: merge this → bump the pin → deploy.
installer-drift is red in that window by design, and its error message says which bump is
missing.

Not in scope

  • Flipping the default origin (one-line follow-up after prod verification).
  • patch.ps1 for native Windows — the hosting side already supports it; left out rather
    than shipped untested from a macOS box.

…thub.com

The documented one-liner becomes

    curl -fsSL https://install.socket.dev/patch | sh

replacing a raw.githubusercontent.com URL that asked users to trust a
third-party CDN for a script they pipe into a shell, and that is the first
URL a locked-down egress policy blocks. install.socket.dev is a name Socket
controls, already inside the trust boundary a customer grants socket.dev.

What the host serves is a byte-for-byte copy of scripts/install.sh, with its
SHA-256 alongside at /patch.sha256 — the README tells people to diff it, so
that has to hold literally. The GitHub raw URL keeps working and serves the
same bytes, for anyone who would rather not depend on the Socket domain.

Archives can come from Socket too. New SOCKET_PATCH_BASE_URL points the
downloads at any releases base answering GitHub's two asset paths,
`<base>/latest/download/<file>` and `<base>/download/v<ver>/<file>`:

    curl -fsSL https://install.socket.dev/patch \
      | SOCKET_PATCH_BASE_URL=https://install.socket.dev/SocketDev/socket-patch/releases sh

install.socket.dev relays exactly those paths from the GitHub release
(SocketDev/depscan#23840), which is why one template covers both origins and
the script needs no branching. A new socket-patch RELEASE needs no publish
for any of this: the origin resolves "latest" per request against the
upstream release, so nothing runs at release time.

The default origin stays GitHub here. Flipping it is one line, held until the
relay is verified in prod — a script defaulting to a host that does not answer
yet is a broken installer for everyone running it from a git checkout or the
raw URL, and CI's end-to-end install step would fail on main immediately.

Also adds SOCKET_PATCH_INSTALL_DIR, which wins over both existing defaults:
what unprivileged installs into a toolchain-managed prefix need, and what
makes the script testable without writing to a system path.

The trust model is unchanged and the docs are careful not to imply otherwise:
binaries still come from the GitHub release and are still verified against its
SHA256SUMS, and nothing is signed. Whichever origin serves the bytes, the
checksums come from that same origin. Hosting moved who serves the script,
nothing more.

Four gaps closed around the artifact users are told to pipe into a shell:

* install.sh was only shellchecked, never run. CI now installs with it end to
  end and execs the result — twice, once with the default origin and once
  through SOCKET_PATCH_BASE_URL, so the URL template is covered too.
* A third CI step installs through install.socket.dev and asserts the
  installed version matches what that host reports as latest. It skips itself
  with a notice until the host resolves, so it is inert until the relay ships
  rather than red from merge.
* Nothing kept the URL consistent across the README, the script's own usage
  comment, and the runbook; a grep guard fails if any of them drifts.
* Nothing checked the HOSTED copy. The new `installer-drift` workflow (weekly
  + dispatch) diffs the served bytes against scripts/install.sh, verifies the
  published checksum, and shellchecks what is actually served. Deliberately
  not part of CI: it tests a deployed artifact, so a red run means "bump the
  submodule pin in depscan", not "this PR is broken". It also names the
  specific failure this design is exposed to — a Cloudflare bot challenge,
  which would otherwise feed an HTML interstitial to sh.

docs/installer-hosting.md is the runbook for the part that is not obvious from
this repository: the hosted copy is published out of depscan's vendored
submodule pin, so an installer change here goes live on a submodule bump plus
a deploy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mikolalysenko Mikola Lysenko (mikolalysenko) changed the title feat(install): install from install.socket.dev/patch — trusted origin for the curl|sh installer feat(install): install from install.socket.dev, optionally without github.com Aug 6, 2026

@jdalton John-David Dalton (jdalton) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three things, all inline. The big one: pointing SOCKET_PATCH_BASE_URL at the host adds Socket's stack to the trust set rather than dropping GitHub, and SHA256SUMS rides the same origin as the archive. docs/installer-hosting.md says the trust model is unchanged, which holds for the default only.

One not inline, since the file is not in the diff: SOCKET_UPDATE_BASE_URL drops the https-only redirect rule in update/release.rs too.

Comment thread scripts/install.sh
Comment on lines +34 to +40
RELEASES_BASE="${SOCKET_PATCH_BASE_URL:-https://github.com/${REPO}/releases}"
while :; do
case "$RELEASES_BASE" in
*/) RELEASES_BASE="${RELEASES_BASE%/}" ;;
*) break ;;
esac
done

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOCKET_PATCH_BASE_URL is never checked for https, so http:// is taken without a word. download() is a plain curl -fsSL too, no --proto-redir. Both legs matter here, since SHA256SUMS comes down the same way.

Suggested change
RELEASES_BASE="${SOCKET_PATCH_BASE_URL:-https://github.com/${REPO}/releases}"
while :; do
case "$RELEASES_BASE" in
*/) RELEASES_BASE="${RELEASES_BASE%/}" ;;
*) break ;;
esac
done
RELEASES_BASE="${SOCKET_PATCH_BASE_URL:-https://github.com/${REPO}/releases}"
# Refuse a non-HTTPS base: the archive and the SHA256SUMS that verifies it are
# both fetched from here, so plaintext would forfeit both.
case "$RELEASES_BASE" in
https://*) ;;
*)
echo "SOCKET_PATCH_BASE_URL must be an https:// URL, got: $RELEASES_BASE" >&2
exit 1
;;
esac
while :; do
case "$RELEASES_BASE" in
*/) RELEASES_BASE="${RELEASES_BASE%/}" ;;
*) break ;;
esac
done

Comment thread .github/workflows/ci.yml
Comment on lines +129 to +132
if ! curl -sfI -m 20 https://install.socket.dev/patch/latest >/dev/null 2>&1; then
echo "::notice::install.socket.dev/patch/latest does not answer yet — skipping the Socket-origin install."
exit 0
fi

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curl -sfI fails on a 403, so a challenge makes this print the skip notice and pass green. It turns itself off on the one thing it is here to catch. Only a resolve failure should skip, and that code wants a name rather than sitting bare.

Suggested change
if ! curl -sfI -m 20 https://install.socket.dev/patch/latest >/dev/null 2>&1; then
echo "::notice::install.socket.dev/patch/latest does not answer yet — skipping the Socket-origin install."
exit 0
fi
rc=0
code=$(curl -sI -m 20 -o /dev/null -w '%{http_code}' https://install.socket.dev/patch/latest) || rc=$?
# curl exit 6 is "could not resolve host": the host is not up yet.
if [ "$rc" -eq 6 ]; then
echo "::notice::install.socket.dev does not resolve yet, skipping the Socket-origin install."
exit 0
fi
if [ "$rc" -ne 0 ]; then
echo "::error::probing install.socket.dev failed (curl exit $rc)" >&2
exit 1
fi
if [ "$code" != "200" ]; then
echo "::error::install.socket.dev/patch/latest answered $code, not 200 (a challenge would feed HTML to sh)" >&2
exit 1
fi

Comment thread .github/workflows/ci.yml
Comment on lines +149 to +154
for f in README.md scripts/install.sh docs/installer-hosting.md; do
if ! grep -qF 'https://install.socket.dev/patch' "$f"; then
echo "Error: $f no longer references https://install.socket.dev/patch" >&2
exit 1
fi
done

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails unless the README already has the new URL, so the flip has to merge before DNS and the deploy exist. Anyone following main in that window gets a DNS error on the documented command. Could it skip while the host does not resolve, like the step above?

Comment thread docs/installer-hosting.md

## What the trust model actually is

Unchanged by the hosting move, and worth being precise about:

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True for the default origin, not for the one the README recommends. Pointing the base URL at the host adds Socket's stack to the trust set, and the checksums come from the same place as the archive.

Suggested change
Unchanged by the hosting move, and worth being precise about:
Unchanged for the default origin. Pointing `SOCKET_PATCH_BASE_URL` (or
`SOCKET_UPDATE_BASE_URL`) at `install.socket.dev` does change it: GitHub stays in the
trust set, because the relay pins nothing and re-fetches upstream per request, and
Socket's serving stack joins it. The checksums are relayed from the same origin as the
archive, so they are not an independent check of it. Worth being precise about:

Comment thread docs/installer-hosting.md
Comment on lines +82 to +92
The publish path lives in [depscan][depscan], which vendors this repository as
`submodules/socket-patch`:

1. A change to `scripts/install.sh` merges **here**.
2. depscan's `submodules/socket-patch` pin is bumped to that commit.
3. depscan's prod deploy runs its **Publish install.socket.dev site** step,
which copies `submodules/socket-patch/scripts/install.sh` to
`gs://socket-install-prod/patch`, publishes its sha256 and the landing page,
then re-reads the object and fails the deploy if the bytes do not match.
4. `install-server` (a `gcs-bucket-server` instance, `tanka/lib/depscan/install-server.libsonnet`)
serves that bucket at `install.socket.dev`.

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repo is public, and this spells out the whole publish path for a curl | sh endpoint: which repo, which identity, which bucket, and that a pin bump is the gate. The long version already lives in the internal doc. Suggest keeping the drift contract and dropping the rest.

Suggested change
The publish path lives in [depscan][depscan], which vendors this repository as
`submodules/socket-patch`:
1. A change to `scripts/install.sh` merges **here**.
2. depscan's `submodules/socket-patch` pin is bumped to that commit.
3. depscan's prod deploy runs its **Publish install.socket.dev site** step,
which copies `submodules/socket-patch/scripts/install.sh` to
`gs://socket-install-prod/patch`, publishes its sha256 and the landing page,
then re-reads the object and fails the deploy if the bytes do not match.
4. `install-server` (a `gcs-bucket-server` instance, `tanka/lib/depscan/install-server.libsonnet`)
serves that bucket at `install.socket.dev`.
The hosted copy is published by Socket's deployment pipeline from a reviewed pin of this
repository, so an installer change here goes live once that pin is bumped and deployed.
The public repository needs no write credentials into Socket infrastructure, and the pin
bump is a reviewed change, so nothing reaches a `curl | sh` endpoint without review on
both sides.

Comment thread README.md
Comment on lines +34 to +36
On a network that blocks or distrusts `github.com`, set `SOCKET_PATCH_BASE_URL` so the
archives come from Socket too — `install.socket.dev` relays them from the GitHub release,
checksums included:

@jdalton John-David Dalton (jdalton) Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"checksums included" reads as reassurance, but it is the weak part: they come from the same origin as the archive, so they do not vouch for it on their own. One clause so a reader knows what they are trusting.

Suggested change
On a network that blocks or distrusts `github.com`, set `SOCKET_PATCH_BASE_URL` so the
archives come from Socket too — `install.socket.dev` relays them from the GitHub release,
checksums included:
On a network that blocks or distrusts `github.com`, set `SOCKET_PATCH_BASE_URL` so the
archives come from Socket too. `install.socket.dev` relays them from the GitHub release,
checksums included - which also means the checksums come from the relay rather than
independently, so this trades GitHub's origin for Socket's rather than adding a check:

Comment thread scripts/install.sh
# curl -fsSL .../install.sh | SOCKET_PATCH_VERSION=3.0.0 sh
# curl -fsSL https://install.socket.dev/patch | SOCKET_PATCH_VERSION=3.0.0 sh
#
# Override where the archives come from with SOCKET_PATCH_BASE_URL — a releases

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit, take it or leave it: 22 em-dashes and one en-dash across the diff. Plain hyphens read the same, type easier, and grep. Nothing here enforces it, so entirely your call.

Suggested change
# Override where the archives come from with SOCKET_PATCH_BASE_URL a releases
# Override where the archives come from with SOCKET_PATCH_BASE_URL - a releases

The rest: install.sh:115, README.md:35,44, docs/installer-hosting.md:10,27,48,72,123,125 (plus the en-dash in "steps 2-3" at :111), .github/workflows/ci.yml:98,104,111,123,130,144, .github/workflows/installer-drift.yml:4,46,74, CHANGELOG.md:342,350,359.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants