Skip to content

feat(installer): Lemonade daemon bootstrap (config + systemd unit) (PR-5)#159

Merged
thinmintdev merged 1 commit into
mainfrom
feat/lemonade-daemon-bootstrap-pr5
May 23, 2026
Merged

feat(installer): Lemonade daemon bootstrap (config + systemd unit) (PR-5)#159
thinmintdev merged 1 commit into
mainfrom
feat/lemonade-daemon-bootstrap-pr5

Conversation

@thinmintdev
Copy link
Copy Markdown
Contributor

PR-5 of the v0.2 Lemonade migration sequence. Layers the actual lemond
daemon bootstrap on top of PR-4's system prerequisites (#157), still
landing before PR-6's server_models.json generator block.

Summary

  • Pinned Lemonade embeddable tarball v10.6.0, sha256-verified with
    HAL0_SKIP_LEMONADE_SHA=1 escape hatch (mirrors PR-4's FLM .deb pin
    style), extracted to /opt/lemonade/ with a marker file so re-runs
    skip the ~200 MB download when the binary on disk already matches
    LEMONADE_VERSION.
  • New idempotent hal0 system user/group; owns /opt/lemonade and
    /var/lib/hal0/lemonade so lemond runs unprivileged per ADR-0008 §1
    (internal loopback-only runtime).
  • Atomic write of /var/lib/hal0/lemonade/config.json from the locked
    baseline in lemonade-adoption-plan §3 — config_version=1, port
    13305 loopback, max_loaded_models=4, rocm_channel="stable",
    flm.args="--asr 1 --embed 1", kokoro.cpu_bin="builtin", etc.
  • Mandatory llamacpp.args = "--parallel 1 --threads N" per
    ADR-0008 §4 + memory hal0_lemonade_threads_deadlock. Formula:
    N = max(2, (nproc - 2) / 4) — splits across the four-process
    capability rollup (primary + embed + rerank + voice) and avoids the
    spike fix(ci): clear ruff lint + format errors blocking CI #2 Vulkan-dispatch deadlock. Defaults to 2 + warn when nproc
    is unavailable; the --threads flag is never omitted.
  • /etc/systemd/system/hal0-lemonade.service verbatim from plan §3
    (Type=simple, User=hal0, LimitMEMLOCK=infinity, CPUQuota=80%,
    ExecStop=/usr/bin/curl ... /internal/shutdown).
  • Service start block extended to systemctl enable --now hal0-lemonade before hal0-api, with a 30 s wait_active. Soft
    on failure — hal0-api still boots + surfaces the dead-lemond banner.
  • Full DEV_MODE skip with what-would-happen logging.
  • ERR-trap recovery hint for the new step.
  • UI_STEP_TOTAL bumped 9 → 10.

Scope

File touched: only installer/install.sh (+298 / -1). PR-4's section
and PR-6's server_models.json block are untouched.

Refs

  • docs/internal/lemonade-adoption-plan-2026-05-22.md §3 (service
    topology + verbatim config.json + unit baseline), §11 PR-5,
    §12.2 (port 13305 loopback), §12.3 (version pinning).
  • docs/internal/adr/0008-lemonade-adoption.md §1 (single lemond,
    loopback), §3 (per-type LRU), §4 (mandatory --threads N).
  • Memory hal0_lemonade_threads_deadlock — non-negotiable
    operational constraint; spike fix(ci): clear ruff lint + format errors blocking CI #2 differential matrix.

Test plan

  • bash -n installer/install.sh clean.
  • shellcheck installer/install.sh — no new findings; pre-existing
    SC1091 / SC2018 / SC2019 info-level only.
  • .venv/bin/python -m pytest tests/ -q — 1479 passed, 8 skipped,
    3 xfailed (pre-existing).
  • .venv/bin/ruff check src tests — all checks passed.
  • .venv/bin/ruff format --check src tests — 245 files formatted.
  • Live sudo bash installer/install.sh on hal0 LXC 105 once
    PR-6/PR-7 land and the config schema settles (real fixture).
  • Bump LEMONADE_SHA256 from the all-zeros placeholder before v0.2
    tag.

🤖 Generated with Claude Code

…R-5)

Adds the `Lemonade daemon` install step after PR-4's system
prerequisites and before PR-6's server_models.json block:

* Pinned embeddable tarball (v10.6.0) — sha256 verified with
  `HAL0_SKIP_LEMONADE_SHA=1` escape hatch, extracted into
  /opt/lemonade/ with a `.installed-version` marker so re-runs skip
  the multi-hundred-MB download when the binary is already current.
* Dedicated `hal0` system user/group (idempotent) — owns
  /opt/lemonade + /var/lib/hal0/lemonade so lemond runs unprivileged
  per ADR-0008 §1 (internal loopback-only runtime).
* /var/lib/hal0/lemonade/config.json written atomically every run
  with the locked baseline from lemonade-adoption-plan §3
  (config_version=1, port 13305 loopback, max_loaded_models=4,
  rocm_channel=stable, flm.args="--asr 1 --embed 1", kokoro
  cpu_bin=builtin, ...).
* MANDATORY `llamacpp.args = "--parallel 1 --threads N"` per
  ADR-0008 §4 + memory `hal0_lemonade_threads_deadlock`. Formula:
  N = max(2, (nproc - 2) / 4) — splits across the four-process
  capability rollup (primary + embed + rerank + voice) and avoids
  the spike #2 Vulkan-dispatch deadlock. Defaults to 2 + warn when
  nproc is unavailable; the flag is never omitted.
* /etc/systemd/system/hal0-lemonade.service — Type=simple, User=hal0,
  LimitMEMLOCK=infinity, CPUQuota=80%, ExecStop curl
  /internal/shutdown for clean drain. Verbatim from plan §3.
* Service start block extended to `systemctl enable --now
  hal0-lemonade` before hal0-api, with a 30 s wait_active.
* Full DEV_MODE skip with what-would-happen logging.
* ERR-trap recovery hint for the new step.
* UI_STEP_TOTAL bumped 9 → 10.

Refs: lemonade-adoption-plan-2026-05-22 §3 / §11 PR-5 / §12.2-12.3,
ADR-0008 §1, §3, §4, memory `hal0_lemonade_threads_deadlock`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thinmintdev thinmintdev merged commit c71f04d into main May 23, 2026
6 checks passed
@thinmintdev thinmintdev deleted the feat/lemonade-daemon-bootstrap-pr5 branch May 23, 2026 02:16
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.

1 participant