contrib: devbox-aware scheduler templates (host-side, docker exec)
On hosts running a long-lived opencode-devbox (or equivalent)
container, mempalace-session lives INSIDE the container, not on
the host. The existing contrib/* templates install a scheduler on
the machine that runs the tool; for the devbox case the scheduler
has to live on the host and reach into the container via
'docker exec'. This was noted in passing in contrib/README.md but
no templates were actually shipped for it.
Adds parallel *-devbox templates for systemd and cron:
contrib/systemd/mempalace-session-devbox.service
- Type=oneshot, same 2h TimeoutStartSec + low Nice as the direct
variant.
- Two Environment knobs (CONTAINER, CONTAINER_USER) default to
opencode-devbox/developer, overrideable via
'systemctl --user edit'.
- ExecCondition checks 'docker ps --filter name= --filter
status=running' so the unit no-ops cleanly when the container
is currently down. systemd reports this as a successful
'condition failed' state — no alert noise across dev cycles
of teardown/rebuild.
- ExecStart is plain /usr/bin/docker exec with no shell; systemd
does the env-var expansion.
- Stdout/stderr go to journalctl --user -u <unit> (nothing to
redirect, since docker exec surfaces container output to the
calling process).
contrib/systemd/mempalace-session-devbox.timer
- Mon 03:00 Persistent=true RandomizedDelaySec=30m, mirrors the
direct timer.
contrib/cron/mempalace-session-devbox.cron
- Equivalent shell-wrapped form for hosts using cron instead of
systemd. 'docker ps | grep -q .' short-circuits if the container
isn't running. Log goes to $HOME/.cache/mempalace-session/
cron-devbox.log on the HOST (outside the container) so it's
inspectable without dropping into the devbox.
contrib/README.md:
- Replaces the two-paragraph 'Running inside a container' note
with a proper section: preconditions, install recipes for both
the systemd and cron devbox variants, verify/uninstall commands,
customization via 'systemctl --user edit', behaviour when the
container is down.
- Chooser table gains a dedicated row pointing devbox users at
the *-devbox templates, and mentions the systemd vs cron pick
for that case.
- New 'When to pick devbox variants vs direct ones' table covers
the rare both-installed case (host mempalace AND in-container
mempalace see separate palaces — they don't cross-pollinate).
Top-level README.md 'Keeping it fresh' subsection gains a quick-start
block for the devbox variant alongside the existing Linux/macOS
quick-starts.
Tested: all four systemd units parse cleanly as INI via
configparser (sections + key=value pairs); validated file sizes
and locations match the layout described in docs. Runtime
validation (systemctl --user enable; actual docker exec) requires
a host with docker + an opencode-devbox container up — deferred
to the user's Mac/Linux boxes.
This commit is contained in:
@@ -263,6 +263,7 @@ Manual invocation is fine while you're actively driving the machine, but long-ru
|
|||||||
- **systemd user timer** (recommended on Linux): survives reboots, catches missed runs, logs to `journalctl`.
|
- **systemd user timer** (recommended on Linux): survives reboots, catches missed runs, logs to `journalctl`.
|
||||||
- **launchd user agent** (recommended on macOS): native-equivalent — logs to `~/Library/Logs/`, single-instance guarantees, `ProcessType=Background` throttling.
|
- **launchd user agent** (recommended on macOS): native-equivalent — logs to `~/Library/Logs/`, single-instance guarantees, `ProcessType=Background` throttling.
|
||||||
- **cron**: simplest, works on BSD and systemd-less distros. No user-unit awareness needed.
|
- **cron**: simplest, works on BSD and systemd-less distros. No user-unit awareness needed.
|
||||||
|
- **Devbox variants** (`*-devbox.*`): if you run `mempalace-session` inside a long-lived container (e.g. [opencode-devbox](https://gitea.jordbo.se/joakimp/opencode-devbox)), the scheduler lives on the **host** and uses `docker exec` to reach the tool inside the container. Systemd and cron variants are included; both guard against "container currently stopped" so the timer is safe to leave enabled across dev cycles.
|
||||||
|
|
||||||
Quick-start (Linux / systemd, weekly Mon 03:00 local):
|
Quick-start (Linux / systemd, weekly Mon 03:00 local):
|
||||||
|
|
||||||
@@ -284,7 +285,20 @@ launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/se.jordbo.mempalace-se
|
|||||||
launchctl enable "gui/$(id -u)/se.jordbo.mempalace-session"
|
launchctl enable "gui/$(id -u)/se.jordbo.mempalace-session"
|
||||||
```
|
```
|
||||||
|
|
||||||
See [`contrib/README.md`](contrib/README.md) for full install/verify/uninstall recipes, tuning, and devbox/container caveats. The full operational routine (triggers, cadence, verification) is in [`ARCHITECTURE.md`](ARCHITECTURE.md) §5.
|
Quick-start (host-side scheduling for a long-running opencode-devbox container):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# systemd on the host → docker exec into the container
|
||||||
|
mkdir -p ~/.config/systemd/user
|
||||||
|
cp contrib/systemd/mempalace-session-devbox.{service,timer} ~/.config/systemd/user/
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now mempalace-session-devbox.timer
|
||||||
|
# If your container isn't named 'opencode-devbox' or its user isn't
|
||||||
|
# 'developer', run `systemctl --user edit mempalace-session-devbox.service`
|
||||||
|
# to set CONTAINER / CONTAINER_USER via an override.
|
||||||
|
```
|
||||||
|
|
||||||
|
See [`contrib/README.md`](contrib/README.md) for full install/verify/uninstall recipes, tuning, chooser table (host vs. devbox), and devbox/container caveats. The full operational routine (triggers, cadence, verification) is in [`ARCHITECTURE.md`](ARCHITECTURE.md) §5.
|
||||||
|
|
||||||
### Containerized (devbox) notes
|
### Containerized (devbox) notes
|
||||||
|
|
||||||
|
|||||||
+100
-10
@@ -184,25 +184,115 @@ crontab -e # remove the mempalace-session line by hand
|
|||||||
|
|
||||||
| Situation | Pick |
|
| Situation | Pick |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Desktop / laptop, modern systemd-based Linux distro | systemd user timer |
|
| Desktop / laptop, mempalace installed directly on the host, modern systemd-based Linux | `systemd/mempalace-session.{service,timer}` |
|
||||||
| macOS (any recent version) | launchd user agent |
|
| macOS (any recent version), mempalace on the host | `launchd/se.jordbo.mempalace-session.plist` |
|
||||||
| Long-running Linux devbox or server, wants "Persistent=true" catch-up | systemd user timer |
|
| Long-running Linux devbox or server, mempalace on the host | `systemd/mempalace-session.{service,timer}` |
|
||||||
| BSD, Alpine, or Linux distro without systemd | cron |
|
| **opencode-devbox (or similar) container with mempalace *inside* it** | **`systemd/mempalace-session-devbox.{service,timer}`** (preferred) or `cron/mempalace-session-devbox.cron` (simpler) |
|
||||||
| You already have a cron-based job scheduler on the box | cron |
|
| BSD, Alpine, or Linux distro without systemd | `cron/mempalace-session.cron` |
|
||||||
|
| You already have a cron-based job scheduler on the box | any `cron/*.cron` template |
|
||||||
| You want logs in `journalctl` (Linux) or Console.app (macOS) rather than a file | systemd user timer / launchd |
|
| You want logs in `journalctl` (Linux) or Console.app (macOS) rather than a file | systemd user timer / launchd |
|
||||||
|
|
||||||
If you're not sure: **systemd on Linux, launchd on macOS, cron only when neither is available**. All three wrap the same `mempalace-session` command — the difference is purely in *how* the box remembers to run it.
|
If you're not sure: **systemd on Linux, launchd on macOS, cron only when neither is available. Use the `-devbox` variant when mempalace lives inside a long-running container rather than on the host.** All templates wrap the same `mempalace-session` command — the difference is purely in *where* the scheduler lives and whether it needs to `docker exec` into a container to reach the tool.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Running inside a container (devbox)
|
## Running inside a container (devbox)
|
||||||
|
|
||||||
Inside a Docker-based devbox, neither systemd nor cron typically runs by default. Two options:
|
If you run opencode inside a long-lived container like [opencode-devbox](https://gitea.jordbo.se/joakimp/opencode-devbox), neither systemd nor cron is running inside that container — they're host-level services. The correct pattern is **host-side scheduling that `docker exec`s into the running container**. The `*-devbox` templates in `contrib/systemd/` and `contrib/cron/` implement exactly this.
|
||||||
|
|
||||||
1. **Schedule on the host, not the container** — have the host run `docker exec -u <user> <container> mempalace-session` on a timer. The container must be long-running (not per-invocation) for this to work.
|
Preconditions:
|
||||||
2. **Run a systemd-in-container setup** — viable but usually not worth the complexity for this alone.
|
|
||||||
|
|
||||||
For most devbox users, a simple weekly manual run via `mempalace-session` (or a host-side cron that shells into the container) is the pragmatic choice. The tool is cheap enough that skipping a week costs nothing — dedup will catch up on the next run.
|
- **Long-lived container.** `docker compose up -d` with `restart: unless-stopped` (or equivalent). If the container is ephemeral (per-invocation), this pattern doesn't apply.
|
||||||
|
- **`mempalace-session` is already installed inside the container.** opencode-devbox bakes it in via `mempalace-toolkit`, so a running devbox already satisfies this.
|
||||||
|
- **Host user can talk to docker.** Member of the `docker` group on Linux, or Docker Desktop running under the current login session on macOS.
|
||||||
|
- **Canonical container name is `opencode-devbox`.** If you renamed it via `container_name:` or docker-compose project naming, adjust `CONTAINER` in the template.
|
||||||
|
|
||||||
|
Both devbox templates guard against "container currently stopped" — they no-op silently if `docker ps` shows no running container with the expected name. That makes the timer safe to leave enabled across dev cycles where you tear the container down and bring it back up.
|
||||||
|
|
||||||
|
### systemd user timer (host-side, devbox variant)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
mkdir -p ~/.config/systemd/user
|
||||||
|
cp contrib/systemd/mempalace-session-devbox.service ~/.config/systemd/user/
|
||||||
|
cp contrib/systemd/mempalace-session-devbox.timer ~/.config/systemd/user/
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now mempalace-session-devbox.timer
|
||||||
|
|
||||||
|
# Keep the timer running across logout (typical on dev hosts)
|
||||||
|
sudo loginctl enable-linger "$USER"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Customize container name / user** (if you don't use the defaults):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user edit mempalace-session-devbox.service
|
||||||
|
# In the override that opens, set:
|
||||||
|
# [Service]
|
||||||
|
# Environment=CONTAINER=my-devbox-name
|
||||||
|
# Environment=CONTAINER_USER=my-user
|
||||||
|
```
|
||||||
|
|
||||||
|
Or edit the shipped service file in place before copying.
|
||||||
|
|
||||||
|
**Verify:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user list-timers mempalace-session-devbox.timer
|
||||||
|
systemctl --user status mempalace-session-devbox.service
|
||||||
|
journalctl --user -u mempalace-session-devbox --since today
|
||||||
|
|
||||||
|
# Force a run right now (while the container is up)
|
||||||
|
systemctl --user start mempalace-session-devbox.service
|
||||||
|
```
|
||||||
|
|
||||||
|
**Behaviour when the container is down:** `ExecCondition` fails, service is marked "condition failed" (considered a successful no-op by systemd), no alert noise. Bring the container back up and the next scheduled fire will run normally.
|
||||||
|
|
||||||
|
**Uninstall:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user disable --now mempalace-session-devbox.timer
|
||||||
|
rm ~/.config/systemd/user/mempalace-session-devbox.{service,timer}
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### cron (host-side, devbox variant)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Read the template — it has CONTAINER / CONTAINER_USER at the top.
|
||||||
|
# Adjust if your setup differs from opencode-devbox defaults.
|
||||||
|
cat contrib/cron/mempalace-session-devbox.cron
|
||||||
|
|
||||||
|
# Install (preserves existing crontab entries)
|
||||||
|
(crontab -l 2>/dev/null; cat contrib/cron/mempalace-session-devbox.cron) | crontab -
|
||||||
|
|
||||||
|
# Ensure the log directory exists
|
||||||
|
mkdir -p ~/.cache/mempalace-session
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
crontab -l | grep mempalace-session-devbox
|
||||||
|
tail -f ~/.cache/mempalace-session/cron-devbox.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Uninstall:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e # remove the mempalace-session-devbox line by hand
|
||||||
|
```
|
||||||
|
|
||||||
|
### When to pick devbox variants vs direct ones
|
||||||
|
|
||||||
|
| Setup | Templates to use |
|
||||||
|
|---|---|
|
||||||
|
| mempalace installed directly on the host (no devbox) | `mempalace-session.{service,timer}`, `mempalace-session.cron`, `se.jordbo.mempalace-session.plist` |
|
||||||
|
| opencode-devbox container is where opencode + mempalace live | `mempalace-session-devbox.{service,timer}`, `mempalace-session-devbox.cron` |
|
||||||
|
| Both (rare — mempalace on host AND inside a separate devbox) | Install both; they write to separate palaces. |
|
||||||
|
|
||||||
|
The in-container mempalace sees only the container's opencode.db and palace (via named volumes). The host's mempalace, if installed, sees only the host's. Two parallel palaces don't cross-pollinate — decide where you want the source of truth to live and schedule accordingly.
|
||||||
|
|
||||||
|
### Alternative not documented here
|
||||||
|
|
||||||
|
"Run systemd inside the container" is technically viable (systemd-in-docker images exist) but adds non-trivial complexity for the sake of a once-weekly batch job. The host-scheduled approach above is equivalent in outcome and much simpler. Skip systemd-in-container unless you already have other reasons to need it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Sample crontab entry for mempalace-session inside a long-running
|
||||||
|
# opencode-devbox container, scheduled from the DOCKER HOST (not inside
|
||||||
|
# the container — containers typically don't run cron).
|
||||||
|
#
|
||||||
|
# Host-side cron runs `docker exec` against the running container once a
|
||||||
|
# week. Requires:
|
||||||
|
# - Container is long-lived (compose up, restart: unless-stopped, etc.)
|
||||||
|
# - User running cron has rights to talk to the docker socket
|
||||||
|
# (usually means being in the `docker` group, or on macOS having
|
||||||
|
# Docker Desktop running for the current login session)
|
||||||
|
# - `mempalace-session` is already installed inside the container
|
||||||
|
# (opencode-devbox bakes it in via cli_utils → mempalace-toolkit)
|
||||||
|
#
|
||||||
|
# Install:
|
||||||
|
# # Replace CONTAINER / USER / HOST_USER to match your setup, then:
|
||||||
|
# (crontab -l 2>/dev/null; cat contrib/cron/mempalace-session-devbox.cron) | crontab -
|
||||||
|
#
|
||||||
|
# Adjust CONTAINER and USER below before installing.
|
||||||
|
#
|
||||||
|
# Design notes:
|
||||||
|
# - `docker ps --filter name=... --filter status=running` makes the job a
|
||||||
|
# no-op if the container is down, so the timer is harmless on machines
|
||||||
|
# where the devbox is currently stopped. No mail/warning/noise.
|
||||||
|
# - Exec runs as `developer` (the opencode-devbox user). Change `-u`
|
||||||
|
# if you named your user something else.
|
||||||
|
# - Output is captured in a log under the HOST user's home, not inside
|
||||||
|
# the container — so you can inspect it from outside.
|
||||||
|
|
||||||
|
CONTAINER=opencode-devbox
|
||||||
|
CONTAINER_USER=developer
|
||||||
|
|
||||||
|
# HOST_USER is used only to anchor the log path. Replace with your host
|
||||||
|
# username (or leave the $HOME substitution if your cron implementation
|
||||||
|
# expands it — most do).
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
|
# m h dom mon dow command
|
||||||
|
0 3 * * 1 /bin/sh -c 'docker ps --filter "name=^/${CONTAINER}$" --filter "status=running" -q | grep -q . && docker exec -u "${CONTAINER_USER}" "${CONTAINER}" mempalace-session >> "$HOME/.cache/mempalace-session/cron-devbox.log" 2>&1'
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Mine opencode session history into MemPalace (inside opencode-devbox container)
|
||||||
|
Documentation=https://gitea.jordbo.se/joakimp/mempalace-toolkit
|
||||||
|
# Only run if docker is reachable AND the container is currently up.
|
||||||
|
# The ExecCondition below gives us the "container running" check at fire
|
||||||
|
# time; these unit-level conditions just skip pointless scheduling churn
|
||||||
|
# on boxes where docker isn't even installed.
|
||||||
|
ConditionPathExists=/usr/bin/docker
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
# Environment knobs — set these in
|
||||||
|
# ~/.config/systemd/user/mempalace-session-devbox.service.d/override.conf
|
||||||
|
# via `systemctl --user edit mempalace-session-devbox.service`
|
||||||
|
# (or just edit this file in place before installing).
|
||||||
|
Environment=CONTAINER=opencode-devbox
|
||||||
|
Environment=CONTAINER_USER=developer
|
||||||
|
|
||||||
|
# Skip cleanly if the container isn't running. ExecCondition failing
|
||||||
|
# marks the unit as "condition failed" (success from systemd's POV, no
|
||||||
|
# alert noise). If this Condition passes, ExecStart fires.
|
||||||
|
ExecCondition=/bin/sh -c 'docker ps --filter "name=^/${CONTAINER}$" --filter "status=running" -q | grep -q .'
|
||||||
|
ExecStart=/usr/bin/docker exec -u ${CONTAINER_USER} ${CONTAINER} mempalace-session
|
||||||
|
|
||||||
|
# Host-side log (journalctl --user -u mempalace-session-devbox --since today)
|
||||||
|
# captures the captured stdout/stderr from the `docker exec` above.
|
||||||
|
|
||||||
|
# 2h runaway ceiling — matches the in-container service unit. Enough for
|
||||||
|
# a cold-start mine on a large corpus plus post-mine repair.
|
||||||
|
TimeoutStartSec=7200
|
||||||
|
|
||||||
|
# Low priority on the host side. The work actually happens inside the
|
||||||
|
# container; the host-side dockerd call is negligible.
|
||||||
|
Nice=5
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Weekly opencode-devbox → MemPalace session mine (host-scheduled)
|
||||||
|
Documentation=https://gitea.jordbo.se/joakimp/mempalace-toolkit
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
# Every Monday at 03:00 local time. Adjust to taste.
|
||||||
|
# See `systemctl --user list-timers mempalace-session-devbox.timer` for
|
||||||
|
# the resolved next-run time after enabling.
|
||||||
|
OnCalendar=Mon 03:00
|
||||||
|
# If the host was off at the scheduled time, run at next boot.
|
||||||
|
Persistent=true
|
||||||
|
# Randomize up to 30 minutes to spread load if you fleet this.
|
||||||
|
RandomizedDelaySec=30m
|
||||||
|
AccuracySec=1m
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Reference in New Issue
Block a user