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.
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
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
Then apt takes over.
sudo apt update
sudo apt install spectroscope
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
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.
| package | spectroscope |
|---|---|
| version | 0.5.0 (a 2026-07-31 snapshot; the channel now carries 0.12.0) |
| architecture | amd64 |
| size | 187306672 bytes (178.63 MiB) |
| installed size | 546947 KiB (534.1 MiB on disk) |
| sha256 | 7f25fd52c5a29ab8cc6e955f5b165ce9b2fa513aa9b8e086c6a88ab8234df62f |
| filename | pool/main/s/spectroscope/spectroscope_0.5.0_amd64.deb |
| description | the agent orchestrator you can watch. The Electron desktop shell, which spawns and supervises the spectro-server JVM. |
| served from | the 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.