See Shared SBCL daemon for opt-in multi-terminal sessions in one image, and Idle power and shell sessions for wakeup measurements.
cclsh has two supported host-Lisp paths:
- CCL 1.13 on Linux x86-64 provides the Nix package, release archives, saved image builds, the prewarmed launcher and the shared login-shell procedure.
- SBCL on macOS or Linux provides a portable source launcher for normal interactive use, scripts, pipelines and Lisp development.
The Nix flake is the recommended Linux installation. Source launchers are useful for development and for hosts where the CCL image is not available.
cclsh is not a POSIX shell. Use it as an ordinary shell before making it a login shell, and keep a working recovery shell available.
Run cclsh without installing it:
nix run github:lambda-symbolics/cclshTo check the package without starting an interactive session:
nix run github:lambda-symbolics/cclsh -- --versionnix profile install github:lambda-symbolics/cclsh
cclsh --versionFrom a checkout, use nix run . or nix profile install .#cclsh. Run nix
flake check to build the complete package and test its command boundary and
installed Quicklisp state.
The flake rebuilds CCL 1.13 from the exact downstream CCL commits and editor
dependency revisions recorded in dependencies.lock. It installs an ordinary
user command; it does not edit /etc/shells or change an account’s login shell.
Each GitHub release includes cclsh-VERSION-linux-x86_64.tar.xz and its
.sha256 checksum. The archive is self-contained: it contains CCLSH’s matched
kernel and image plus the runtime they need, so Nix is not required.
sha256sum -c cclsh-VERSION-linux-x86_64.tar.xz.sha256
tar -xJf cclsh-VERSION-linux-x86_64.tar.xz
mkdir -p ~/.local/opt
mv cclsh-VERSION-linux-x86_64 ~/.local/opt/cclsh
~/.local/opt/cclsh/bin/cclsh --versionKeep bin, lib and libexec together. To make it available as cclsh,
add ~/.local/opt/cclsh/bin to PATH or create a symlink in a personal bin
directory. The optional cclsh-fast launcher is included but remains
experimental.
A saved image uses ~/quicklisp when it already exists. Otherwise cclsh copies
its embedded, sanitized Quicklisp client and distribution index on first use to:
${XDG_DATA_HOME:-$HOME/.local/share}/cclsh/quicklisp
Set CCLSH_QUICKLISP_HOME to choose another writable location. An existing
explicit target must contain setup.lisp; invalid or unwritable targets are
reported and refused.
Interactive sessions and configured -lc=/-ic= commands load the selected
Quicklisp home’s local-init files. Plain -c commands, scripts, piped input
and safe mode do not execute them.
The scripts/cclsh-sbcl launcher runs the checkout with SBCL. It uses
Quicklisp for the small host layer (cffi, bordeaux-threads and
trivial-gray-streams), retains cclsh’s normal syntax and user startup file,
and does not require a C compiler or a saved image.
On macOS, install SBCL with Homebrew; Linux distributions normally package it:
brew install sbcl # macOS
# or: sudo apt install sbcl # Debian/UbuntuInstall Quicklisp once if ~/quicklisp/setup.lisp does not already exist:
curl -O https://beta.quicklisp.org/quicklisp.lisp
sbcl --noinform --non-interactive \
--load quicklisp.lisp \
--eval '(quicklisp-quickstart:install)' \
--eval '(uiop:quit)'Clone cclsh and its locked sibling source dependencies. The same layout works for the CCL source path below:
git clone https://github.com/lambda-symbolics/cclsh.git cclsh
cd cclsh
git clone https://github.com/lambda-symbolics/clinedi.git ../clinedi
git clone https://github.com/lambda-symbolics/structlisp.git ../structlisp
git clone https://github.com/lambda-symbolics/cl-colorist.git ../cl-colorist
git clone https://github.com/lambda-symbolics/colorlisp.git ../colorlisp
git clone https://github.com/lambda-symbolics/colordiff.git ../colordiff
for dependency in clinedi structlisp cl-colorist colorlisp colordiff; do
commit=$(sed -n "s/^$dependency=//p" dependencies.lock)
git -C "../$dependency" checkout --detach "$commit"
done
scripts/cclsh-sbcl --version
scripts/cclsh-sbcl
make sbcl-checkThe launcher deliberately starts SBCL without its own user or system init
files, then loads the selected Quicklisp setup and cclsh. This avoids accidental
interference from unrelated Lisp projects. Use CCLSH_SBCL to select a
specific SBCL executable and CCLSH_QUICKLISP_SETUP for a nonstandard
Quicklisp location. The existing CCLSH_*_SOURCE dependency overrides also
apply when a sibling checkout lives elsewhere.
To expose this checkout as cclsh while keeping its sibling dependencies in
place, symlink the launcher into a personal bin directory:
mkdir -p ~/.local/bin
ln -sf "$PWD/scripts/cclsh-sbcl" ~/.local/bin/cclshThe SBCL launcher is a source runner, not a saved application: it has no
cclsh-fast mode and is not the supported target for /etc/shells or a login
shell. Its edit command cannot yet use CCL’s source-note database. Use the
CCL distribution below when those deployment modes or source-aware edit
matter.
Source development is tested with CCL 1.13 on Linux x86-64. It requires:
- Git and Quicklisp
- GNU make, coreutils, findutils, grep, awk, util-linux, m4 and binutils
- a C toolchain
flock,runuserandgetentfor installation tooling
zoxide is optional. Install fzf as well to use zoxide’s interactive zi
command.
Clone Clinedi, StructLisp, cl-colorist, ColorLisp and Colordiff beside the cclsh checkout, then select their locked revisions:
git clone https://github.com/lambda-symbolics/clinedi.git ../clinedi
git clone https://github.com/lambda-symbolics/structlisp.git ../structlisp
git clone https://github.com/lambda-symbolics/cl-colorist.git ../cl-colorist
git clone https://github.com/lambda-symbolics/colorlisp.git ../colorlisp
git clone https://github.com/lambda-symbolics/colordiff.git ../colordiff
for dependency in clinedi structlisp cl-colorist colorlisp colordiff; do
commit=$(sed -n "s/^$dependency=//p" dependencies.lock)
git -C "../$dependency" checkout --detach "$commit"
doneSet CCLSH_CLINEDI_SOURCE, CCLSH_STRUCTLISP_SOURCE,
CCLSH_CL_COLORIST_SOURCE, CCLSH_COLORLISP_SOURCE or
CCLSH_COLORDIFF_SOURCE when a checkout is not at its default sibling path.
The build refuses a non-Git checkout, local modifications or a revision
mismatch.
For interactive Quicklisp development, register the directory containing the
checkouts in ~/quicklisp/local-init/common-lisp.lisp. Replace the example
path with your checkout parent:
(in-package #:quicklisp-client)
(pushnew #P"/path/to/checkouts/"
*local-project-directories*
:test #'equal)Set CCLSH_QUICKLISP_SETUP when Quicklisp’s setup.lisp is elsewhere. Saved
images include Quicklisp and the five locked source dependencies, but the build
copies only an allowlist of Quicklisp metadata. Personal local init files,
proxy settings, caches and downloaded systems do not enter the image.
Stock CCL is sufficient for regression checks and unsaved REPL development:
scripts/check(require :asdf)
(asdf:load-system "cclsh")
(cclsh:main)A standalone image requires the Linux xstate and cclsh argument-boundary changes from the LuciusMagn CCL fork, based on Clozure CL 1.13. Clone the fork and select the exact supported revision:
git clone https://github.com/luciusmagn/ccl.git ../ccl
git -C ../ccl checkout --detach 579c87300ee632af99182276f2ad40e1c38c5d0a
make ccl-kernel CCL_SOURCE=../ccl
make build CCL=../ccl/lx86cl64 \
CCL_IMAGE=/usr/lib/ccl/lx86cl64.imageThe boot image path is distribution-specific and must match the CCL source
generation. At the selected revision, make ccl-kernel recognizes both changes
as already applied and builds the kernel.
make build validates and activates a matched cclsh kernel and cclsh.image
pair in the repository. It rejects stock CCL with kernel consumed a cclsh
command operand. The files under patches/ are byte-stable mirrors used by
system-shell build attestation. See the patch
ledger for hashes, provenance, validation and upstream status.
Install the validated pair for one user:
make install BINDIR="$HOME/.local/bin"To rebuild and install in one command:
CCLSH_CCL=../ccl/lx86cl64 \
CCLSH_CCL_IMAGE=/usr/lib/ccl/lx86cl64.image \
scripts/installscripts/install creates an owner-only installation in ~/.local/bin by
default. A root make install without DESTDIR is refused; use the shared
system-shell procedure below instead.
The optional cclsh-fast command keeps one CCLSH worker loaded ahead of time.
It accelerates only a no-argument interactive launch whose standard input,
output and error are the same foreground terminal. Options, scripts, piped
input, redirected sessions, login-style invocations and incompatible process
contexts execute the sibling cclsh directly.
The Nix package includes both commands. For a source installation, install
cclsh first, then place the launcher beside its stable path:
make fast
make install-fast BINDIR="$HOME/.local/bin"Start the per-user daemon explicitly, then invoke the alternate command:
cclsh-fast daemon start
cclsh-fast daemon status
cclsh-fastThe daemon requires XDG_RUNTIME_DIR to be an absolute directory owned by the
current user with no group or other permissions. Most systemd login sessions
already provide one. For a development session without one, create a private
directory before starting the daemon:
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-$HOME/.local/run}"
install -d -m 700 "$XDG_RUNTIME_DIR"status prints the ready and active worker counts. It succeeds only when a
matching daemon has at least one ready worker, so scripts can use it as a
readiness probe. restart replaces the daemon and stop shuts it down. Both
operations terminate active accelerated sessions and their terminal jobs.
If no ready compatible daemon answers within the short lease deadline,
cclsh-fast immediately executes ordinary cclsh. Once a lease is committed,
it cannot fall back without risking two shells, so a later proxy failure
returns an error instead.
The client uses the direct runtime for nonstandard inherited signal state or
terminal descriptor flags, an inheritable descriptor above standard error, a
duplicate environment name, a different CPU affinity or resource/security
context, and a nonempty GPG_TTY or SSH_TTY. The last two variables name the
caller’s real terminal and cannot safely bypass the proxy. Workers also require
matching credentials, namespaces, filesystem root, cgroups, LSM label, limits,
scheduling state and exec-sensitive environment variables.
This is deliberately opt-in and is not a login-shell target. It relays the session through a private pseudoterminal, so programs can observe a different terminal and process topology. Detached or session-escaping jobs are not supported: they may be terminated or lose their terminal when the proxy ends. A worker also begins CCL initialization before the caller’s environment and directory are applied. The compatibility checks cannot make every possible process attribute retroactive and are not a sandbox boundary. Use a daemon only inside the same trusted user security domain, and stop it before testing unusual sandbox, capability or terminal setups. Ctrl-Z job control inside CCLSH is relayed, but externally suspending the proxy or worker process itself is not a supported accelerated-session operation.
The ordinary cclsh executable and every noninteractive interface remain
independent of the daemon.
Run the normal checks first:
scripts/checkAfter process, pipeline or terminal changes, rebuild the saved image and run the PTY integration checks:
CCLSH_CCL=../ccl/lx86cl64 \
CCLSH_CCL_IMAGE=/usr/lib/ccl/lx86cl64.image \
scripts/build
CCLSH_CCL=../ccl/lx86cl64 \
CCLSH_CCL_IMAGE=/usr/lib/ccl/lx86cl64.image \
scripts/integration-checkThe integration command also builds and exercises cclsh-fast, including
direct fallback, worker replacement, caller context transfer, Ctrl-C and
terminal restoration.
The shared installer creates one root-owned, user-neutral image and registers a
stable executable path in /etc/shells. It never changes an account’s shell.
Startup, history and writable Quicklisp state remain private to each account.
Run the steps in this order:
scripts/check
make system-shell-build CCL_SOURCE=../ccl CCL=../ccl/lx86cl64 \
CCL_IMAGE=/usr/lib/ccl/lx86cl64.image
make integration-check CCL=../ccl/lx86cl64 \
CCL_IMAGE=/usr/lib/ccl/lx86cl64.image
sudo make install-system-shellsystem-shell-build verifies both downstream CCL changes, rebuilds the kernel,
publishes the image and writes cclsh.attestation over the patch and artifact
hashes. Do not run a plain scripts/build afterward; a plain build removes the
attestation.
install-system-shell validates the candidate before activation, switches its
content-addressed release atomically and registers /usr/local/bin/cclsh in
/etc/shells. It rejects a missing or stale attestation. Registration failure
restores the previous release.
Set PROBE_USER to add a clean-home launch as an existing account:
sudo make install-system-shell PROBE_USER=USERThe probe is additional validation only. It does not bind the installation to
that account. PREFIX, BINDIR and SHELLS_FILE select custom system paths;
their parent directories must be canonical, root-owned and not writable by
non-root users.
Create a private startup file:
install -d -m 700 ~/.config/cclsh
touch ~/.config/cclsh/startup.lisp
chmod 600 ~/.config/cclsh/startup.lispUse the example startup file as a template. If
you have a source checkout, copy it from its absolute path rather than assuming
that examples/ exists in the current directory.
A cclsh login shell does not source /etc/profile or another shell’s
configuration. Ensure that PATH retains required system directories such as
/usr/local/bin, /usr/bin and /bin. Test optional startup tools such as
zoxide separately.
Probe the installed command and configured environment first:
/usr/local/bin/cclsh --version
env CCLSH_SAFE=1 /usr/local/bin/cclsh -c 'exit 0'
/usr/local/bin/cclsh -lc 'echo $PATH'
chsh -s /usr/local/bin/cclshAn administrator can change another account with:
sudo chsh -s /usr/local/bin/cclsh USERPlain -c commands run without user state, as expected by sshd. Executables
must already be visible in sshd’s environment; user-profile-only programs need
an absolute path or a system-visible wrapper. Use -lc when a command should
load startup.lisp. Command strings always use cclsh syntax, not sh syntax.
Keep an existing privileged session open while testing. The conservative choice
is to leave root or another privileged recovery account on a stock shell. If
every privileged account uses cclsh, keep a tested boot-loader or live-system
recovery route.
Only after validating that recovery path should you change root:
sudo chsh -s /usr/local/bin/cclsh rootNever use a repository artifact as an account’s shell; it can disappear after a clean. If startup state fails, bypass it with:
env CCLSH_SAFE=1 /usr/local/bin/cclshFrom a recovery account, restore a stock shell with chsh and verify the result
with getent passwd USER before ending the recovery session.
Rebuild, rerun the integration checks and install another attested release. The
atomic stable path updates every account that uses it. Repeat the clean
--version and -c probes before relying on the new release.