signed apt repository

apt install spectroscope.

This is the signed apt repository for spectroscope, the agent orchestrator you can watch. Pin one key, add one source, and apt keeps the desktop build current the same way it keeps everything else on the machine current.

install

three lines, in this order

The first two lines are the whole client side. They pin the signing key to its own keyring and point one source at one base URL. Nothing here uses trusted=yes, and there is no allow-insecure switch anywhere in this repository.

1 · key

Fetch the public half of the signing key and store it as its own keyring.

curl -fsSL https://apt.spectroscope.dev/spectroscope.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/spectroscope.gpg
2 · source

Add the source, bound to that keyring by signed-by and nothing else.

echo "deb [signed-by=/usr/share/keyrings/spectroscope.gpg] https://apt.spectroscope.dev stable main" \
  | sudo tee /etc/apt/sources.list.d/spectroscope.list
3 · install

Then apt takes over.

sudo apt update
sudo apt install spectroscope
Nobody at the keyboard? Use this line instead. The install line above waits for you twice, and in a container neither question is answered. apt asks you to confirm the download first, and on a machine where tzdata has never been configured a package several levels down the chain then asks which timezone you live in. Neither one times out: stuck at the first, nothing has been downloaded; stuck at the second, spectroscope is unpacked and never configured.
sudo DEBIAN_FRONTEND=noninteractive apt install -y spectroscope
-y answers apt's own confirmation and DEBIAN_FRONTEND=noninteractive answers the timezone question, which then settles on Etc/UTC without telling you. That is the right trade in a container and the wrong one on a machine you are setting up by hand, so it stands next to the three lines rather than replacing them. Only the Ubuntu half is affected: Debian 12 ships tzdata already configured, and its systemd does not recommend the Python network dispatcher that drags tzdata in on Ubuntu. A full Ubuntu server or desktop install is fine too, because tzdata is priority-important and configured long before this repository is added. Minimal container images and chroots are the ones that strip it.

measured 2026-08-03 on ubuntu 24.04 amd64 against this live repository:
the unattended line reaches Setting up spectroscope in 2m 01s, with zero prompts, zero half-configured packages and /etc/timezone left at Etc/UTC

measured 2026-07-31 on debian 12 (bookworm, apt 2.6.1) and ubuntu 24.04.4 LTS, both amd64:
apt update exits 0 with no signature warning, the package installs, dpkg -V re-hashes every installed file without a complaint, and apt remove puts the machine back as it was

amd64 only, for now. There is no arm64 package yet. The arm64 index exists and is covered by the signature, but it is empty, so apt on an arm64 machine will add this source without error and then find nothing to install. There is no arm64 desktop kit either. On arm64 Linux the way in is the CLI zip (spectro-0.5.0.zip) or the plain spectro-server-0.5.0.jar: both run on any JDK 21, and both are attached to the same release.

the package unpacks to /opt/spectroscope and registers /usr/bin/spectroscope through update-alternatives · sudo apt remove spectroscope takes both back off · ~/.spectro holds your sessions, settings and downloaded models, and install and remove both leave it alone

the index

what is in here right now

One package. Everything below is read out of dists/stable/main/binary-amd64/Packages, the file apt downloads and checks against the signature.

packagespectroscope
version0.5.0 (a 2026-07-31 snapshot; the channel now carries 0.12.0)
architectureamd64
size187306672 bytes (178.63 MiB)
installed size546947 KiB (534.1 MiB on disk)
sha2567f25fd52c5a29ab8cc6e955f5b165ce9b2fa513aa9b8e086c6a88ab8234df62f
filenamepool/main/s/spectroscope/spectroscope_0.5.0_amd64.deb
descriptionthe agent orchestrator you can watch. The Electron desktop shell, which spawns and supervises the spectro-server JVM.
served fromthe v0.5.0 release assets, reached by a 302 from the pool path above

the arm64 index is present and signed and contains zero packages (sha256 e3b0c442…, the hash of the empty file)

trust nothing, check everything

how to check this yourself

The point of a signed repository is that you never have to take our word for any of it. Here is what protects the install, and the commands that show you each part working.

the key

One ed25519 key, no expiry, no subkeys. It signs the apt indexes and nothing else. It is deliberately not the Maven signing key, so revoking one surface can never break the other.

spectroscope apt <chris@spectroscope.ai>
E603 2682 4E65 D5CB 3116  02D7 9DF6 0ECC 1605 83D8

read that fingerprint back off the key you just downloaded:

curl -fsSL https://apt.spectroscope.dev/spectroscope.asc \
  | gpg --show-keys --with-fingerprint

what signed-by does

signed-by binds this one keyring to this one source. apt checks the signature on InRelease against that key alone, so a signature from any other key is refused even when it is perfectly valid in itself. Without the flag, any key in apt's global trust store could speak for this repository.

verify the index signature by hand, the same way the publish script does:

curl -fsSL https://apt.spectroscope.dev/spectroscope.asc \
  | gpg --dearmor > /tmp/spectroscope.gpg
curl -fsSL https://apt.spectroscope.dev/dists/stable/InRelease \
  > /tmp/InRelease
gpgv --keyring /tmp/spectroscope.gpg /tmp/InRelease

why the redirect is not a hole

The indexes are served from here. The packages are not: one deb is 178.63 MiB and a Cloudflare static asset may be 25 MiB, so /pool/ answers with a 302 at the GitHub release asset that holds the bytes. The target is looked up in pool-map.json and never guessed, and a filename missing from that map gets a 404 that says so.

apt does not trust a package because of where it came from. It hashes the bytes it received against the sha256 in the index, and that index hangs off the signature you just pinned. The download host is untrusted by construction: it can make an install fail, never quietly change one. A flipped byte with the length preserved is caught as a Hash Sum mismatch, the bytes are quarantined as .deb.FAILED, and nothing is unpacked. A wrong length is caught earlier still.

see the redirect, then check the bytes it points at against the signed index:

curl -sI \
  https://apt.spectroscope.dev/pool/main/s/spectroscope/spectroscope_0.5.0_amd64.deb
# 302, with the release asset in the location header

curl -fsSL https://apt.spectroscope.dev/dists/stable/main/binary-amd64/Packages | grep -E '^(Version|Size|SHA256):'
apt-get download spectroscope && sha256sum spectroscope_*.deb
# the two sha256 values are the same one

the whole pass in one container

scripts/verify-client.sh runs the client side end to end: it serves the tree on loopback, runs the two documented lines, installs, removes, confirms ~/.spectro survived, and finishes with a negative control that corrupts Packages.gz and requires apt to reject it. It exits non-zero at the first step that misbehaves, so CLIENT OK is the only successful ending.

git clone https://github.com/spectroscope/apt.git && cd apt
docker run --rm -v "$PWD":/srv/apt:ro debian:12 sh /srv/apt/scripts/verify-client.sh

the scripts and the worker source are kept out of the served tree (.assetsignore), so they live in the repository, not at this hostname

other doors

other ways to get spectroscope

apt is one Linux door, and it wants a Debian or an Ubuntu on x86_64. The AppImage is the other Linux door. The homebrew cask and the dmg are macOS on Apple silicon. Every packaged desktop build carries its own JRE and its own llama-server, so the machine needs no Java and no Homebrew. The CLI zip and the plain server jar are not desktop builds, and they run anywhere a JDK 21 does.

AppImage

The same desktop build as the deb, in one executable file, for the x86_64 Linux distributions that do not use apt. Nothing installs: mark it executable and run it.

chmod +x spectroscope-0.5.0-x86_64.AppImage
./spectroscope-0.5.0-x86_64.AppImage

Nothing in it is signed, because Linux has no gate to pass. It does carry a published checksum: SHA256SUMS.linux sits beside it on the release and covers this file and the deb.

sha256sum -c SHA256SUMS.linux --ignore-missing

built and smoke-booted on a Linux runner by this project’s own CI

homebrew

One command, and brew upgrade keeps it current. The cask installs the notarized release build.

brew install --cask spectroscope/tap/spectroscope

the cask lives at spectroscope/homebrew-tap

notarized dmg

Signed and notarized, so it opens on double-click without a warning to click through. Drag the app in and you are done.

the latest release carries the dmg, the CLI zip and the plain server jar

from source

Clone and go. The ./spectro launcher resolves a JDK 21+ for you, and the code is MIT.

git clone https://github.com/spectroscope/spectroscope.git
cd spectroscope && ./spectro web start

the release the pool points at

The deb this repository indexes is attached to release v0.5.0. Every other artifact of that cut sits next to it.

the two sites

spectroscope.ai is the product site and the home of the user guide. spectroscope.dev is the developer portal: the five-line facade, the JSONL event contract, the reference.