Container Image

CI publishes a multi-platform image, linux/amd64 and linux/arm64, to the GitHub container registry:

ghcr.io/apache/skywalking-ai-sessionizer

It carries the asz binary and nothing else. The base is distroless, there is no shell, and the process runs as a non-root user. Its working directory is /asz, so the default storage root is /asz/data, which is declared as a volume, and a configuration file placed at /asz/asz.yaml is read the same way it is on a host.

Tags

Tag Points at Moves
0.1.0 that release never
0.1 the newest patch release of that line on each patch
latest the newest release on each release
main the development head on each push to main
sha-<commit> one commit never

A v* tag is a release candidate until the Apache vote has passed. Pushing one publishes only its sha- tag. The version tags are attached when the GitHub release is published, which happens after the vote. A pre-release version such as 0.2.0-rc1 gets its own tag and moves nothing else.

Every image carries org.opencontainers.image.version and org.opencontainers.image.revision labels, and asz version inside it prints the same version.

Serve a storage root

The default command serves the page on port 8787, listening on every interface because a container’s loopback is not reachable from outside.

docker run --rm -p 8787:8787 -v "$PWD/data:/asz/data" \
  ghcr.io/apache/skywalking-ai-sessionizer:latest

There is no Claude Code inside the container, so the page serves what the storage root holds and shows no refresh. This is the way to read a storage root that was collected elsewhere.

Collect from the host

To collect inside the container, mount Claude Code’s directory read-only and tell the adapter where it is. Put a configuration file beside the storage root:

# asz.yaml
storage:
  root: /asz/data
adapters:
  - name: claude-code-local
    enabled: true
    source_root: /claude/projects
    exclude:
      - /private/tmp/**

A file that lists adapters replaces the whole list, so the exclude has to be repeated or Claude Code’s own helper sessions under /private/tmp are collected too. Measured on one machine: 44 sessions with the exclude, 64 without.

docker run --rm -p 8787:8787 \
  -v "$HOME/.claude/projects:/claude/projects:ro" \
  -v "$PWD/asz.yaml:/asz/asz.yaml:ro" \
  -v "$PWD/data:/asz/data" \
  ghcr.io/apache/skywalking-ai-sessionizer:latest

The container user is not the host user, so on Linux the storage root must be writable by it. Running with --user "$(id -u):$(id -g)" is the simplest way. Docker Desktop on macOS maps bind mounts for you.

Run any command

Put the command after the image name. The entrypoint is the binary.

docker run --rm -v "$PWD/data:/asz/data" ghcr.io/apache/skywalking-ai-sessionizer:latest verify
docker run --rm ghcr.io/apache/skywalking-ai-sessionizer:latest glossary
docker run --rm ghcr.io/apache/skywalking-ai-sessionizer:latest version

Build locally

make docker          # -> skywalking-ai-sessionizer:dev

The Dockerfile cross-compiles from the build host, so a multi-platform build needs no emulation and no third-party action. The version is passed as a build argument; make docker passes the nearest git tag.