mirror of
https://github.com/Heretek-AI/heretek-devcontainers.git
synced 2026-11-07 10:56:02 -10:00
Add real socket activation to the quadlets
Each heavy service now has a loopback .socket and a systemd-socket-proxyd service (Requires=/After= the container, --exit-idle-time) on a shifted container port. Container units keep StopWhenUnneeded=true, lose their [Install] section and SecurityLabelType=spc, use Notify=healthy, and the emulator uses AddDevice=/dev/kvm under default container_t confinement. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
This commit is contained in:
11 files changed
+207
-52
No files matched your search
@@ -0,0 +1,16 @@
|
||||
# quadlets/heretek-android-kvm-adb-proxy.service — ADB socket proxy for heretek-android-kvm
|
||||
#
|
||||
# Started by heretek-android-kvm-adb-proxy.socket. Pulls up the emulator unit
|
||||
# (Requires=/After=; Notify=healthy there delays "started" until noVNC
|
||||
# answers) and forwards connections to the container's loopback port. It exits
|
||||
# after 15 idle minutes (--exit-idle-time, systemd-socket-proxyd's idle
|
||||
# timeout); once both android proxies are gone, StopWhenUnneeded=true stops
|
||||
# the emulator.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Android KVM ADB socket proxy (exits when idle)
|
||||
Requires=heretek-android-kvm.service heretek-android-kvm-adb-proxy.socket
|
||||
After=heretek-android-kvm.service heretek-android-kvm-adb-proxy.socket
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time=15min 127.0.0.1:15555
|
||||
@@ -0,0 +1,13 @@
|
||||
# quadlets/heretek-android-kvm-adb-proxy.socket — ADB activation socket for heretek-android-kvm
|
||||
#
|
||||
# Listens on the loopback-only port compose also uses (127.0.0.1:5555) and
|
||||
# activates heretek-android-kvm-adb-proxy.service on the first connection.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Android KVM ADB activation socket (127.0.0.1:5555)
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:5555
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,16 @@
|
||||
# quadlets/heretek-android-kvm-vnc-proxy.service — noVNC socket proxy for heretek-android-kvm
|
||||
#
|
||||
# Started by heretek-android-kvm-vnc-proxy.socket. Pulls up the emulator unit
|
||||
# (Requires=/After=; Notify=healthy there delays "started" until noVNC
|
||||
# answers) and forwards connections to the container's loopback port. It exits
|
||||
# after 15 idle minutes (--exit-idle-time, systemd-socket-proxyd's idle
|
||||
# timeout); once both android proxies are gone, StopWhenUnneeded=true stops
|
||||
# the emulator.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Android KVM noVNC socket proxy (exits when idle)
|
||||
Requires=heretek-android-kvm.service heretek-android-kvm-vnc-proxy.socket
|
||||
After=heretek-android-kvm.service heretek-android-kvm-vnc-proxy.socket
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time=15min 127.0.0.1:16080
|
||||
@@ -0,0 +1,13 @@
|
||||
# quadlets/heretek-android-kvm-vnc-proxy.socket — noVNC activation socket for heretek-android-kvm
|
||||
#
|
||||
# Listens on the loopback-only port compose also uses (127.0.0.1:6080) and
|
||||
# activates heretek-android-kvm-vnc-proxy.service on the first connection.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Android KVM noVNC activation socket (127.0.0.1:6080)
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:6080
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -1,39 +1,52 @@
|
||||
# quadlets/heretek-android-kvm.container — socket-activated Android emulator (Wave 3)
|
||||
# quadlets/heretek-android-kvm.container — on-demand Android emulator (rootless Podman quadlet)
|
||||
#
|
||||
# Heavy KVM emulator from compose/profile-android.yml, expressed as a rootless
|
||||
# Podman systemd quadlet. StopWhenUnneeded=true keeps it at 0 MB idle RAM
|
||||
# until socket activation pulls it up. Image and loopback ports mirror the
|
||||
# compose profile exactly: budtmo/docker-android:emulator_14.0 on
|
||||
# 127.0.0.1:6080 (VNC) and 127.0.0.1:5555 (ADB).
|
||||
# Socket-activated through two loopback sockets, one per published port:
|
||||
# heretek-android-kvm-vnc-proxy.socket 127.0.0.1:6080 (noVNC)
|
||||
# heretek-android-kvm-adb-proxy.socket 127.0.0.1:5555 (ADB)
|
||||
# The first connection to either starts its systemd-socket-proxyd service,
|
||||
# which Requires= this unit, so the emulator is started on demand and proxied
|
||||
# to its container-only ports (127.0.0.1:16080 / 127.0.0.1:15555). When both
|
||||
# proxies have exited after --exit-idle-time, StopWhenUnneeded=true stops the
|
||||
# emulator: 0 MB RAM while idle. No [Install] section on purpose — it must
|
||||
# never start at login.
|
||||
#
|
||||
# Enable + start (user systemd):
|
||||
# Image: budtmo/docker-android:emulator_14.0_v3.8.0-p1, the same immutable
|
||||
# release tag as compose/profile-android.yml.
|
||||
#
|
||||
# KVM: AddDevice=/dev/kvm grants only the KVM device node. The container keeps
|
||||
# the default container_t SELinux confinement (no SecurityLabelType /
|
||||
# SecurityLabelDisable). If an enforcing host denies /dev/kvm access, allow
|
||||
# device use for containers rather than disabling labelling:
|
||||
# sudo setsebool -P container_use_devices=true
|
||||
#
|
||||
# Install (see README "Quadlets"):
|
||||
# cp quadlets/heretek-android-kvm.container ~/.config/containers/systemd/
|
||||
# cp quadlets/heretek-android-kvm-*-proxy.socket quadlets/heretek-android-kvm-*-proxy.service ~/.config/systemd/user/
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now heretek-android-kvm.service
|
||||
# KVM passthrough comes from the Device= mapping below (a device node grant,
|
||||
# not a capability and not a volume mount — no SELinux label rule applies).
|
||||
# systemctl --user enable --now heretek-android-kvm-vnc-proxy.socket heretek-android-kvm-adb-proxy.socket
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Android KVM emulator (socket-activated, 0 MB idle RAM)
|
||||
Description=Heretek Android KVM emulator (socket-activated, stops when idle)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Container]
|
||||
Image=budtmo/docker-android:emulator_14.0
|
||||
Image=budtmo/docker-android:emulator_14.0_v3.8.0-p1
|
||||
ContainerName=heretek-android-kvm
|
||||
User=1000:1000
|
||||
Device=/dev/kvm
|
||||
PublishPort=127.0.0.1:6080:6080
|
||||
PublishPort=127.0.0.1:5555:5555
|
||||
AddDevice=/dev/kvm
|
||||
PublishPort=127.0.0.1:16080:6080
|
||||
PublishPort=127.0.0.1:15555:5555
|
||||
Volume=heretek-android-data:/data:Z
|
||||
Environment=EMULATOR_DEVICE=Samsung Galaxy S10
|
||||
DropCapability=ALL
|
||||
NoNewPrivileges=true
|
||||
SecurityLabelType=spc
|
||||
HealthCmd=bash -c 'exec 3<>/dev/tcp/127.0.0.1/6080'
|
||||
HealthInterval=10s
|
||||
HealthStartPeriod=180s
|
||||
Notify=healthy
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
TimeoutStartSec=600
|
||||
@@ -0,0 +1,15 @@
|
||||
# quadlets/heretek-blender-mcp-proxy.service — socket proxy for heretek-blender-mcp
|
||||
#
|
||||
# Started by heretek-blender-mcp-proxy.socket. Pulls up the container unit
|
||||
# (Requires=/After=; Notify=healthy there delays "started" until the server
|
||||
# answers) and forwards connections to the container's loopback port. It exits
|
||||
# after 15 idle minutes (--exit-idle-time, systemd-socket-proxyd's idle
|
||||
# timeout), which lets StopWhenUnneeded=true stop the container.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Blender MCP socket proxy (exits when idle)
|
||||
Requires=heretek-blender-mcp.service heretek-blender-mcp-proxy.socket
|
||||
After=heretek-blender-mcp.service heretek-blender-mcp-proxy.socket
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time=15min 127.0.0.1:18781
|
||||
@@ -0,0 +1,14 @@
|
||||
# quadlets/heretek-blender-mcp-proxy.socket — activation socket for heretek-blender-mcp
|
||||
#
|
||||
# Listens on the loopback-only port compose also uses (127.0.0.1:8781) and
|
||||
# activates heretek-blender-mcp-proxy.service on the first connection.
|
||||
# This is the only Blender unit that is enabled (WantedBy=sockets.target).
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Blender MCP activation socket (127.0.0.1:8781)
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:8781
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -1,34 +1,46 @@
|
||||
# quadlets/heretek-blender-mcp.container — socket-activated Blender MCP server (Wave 3)
|
||||
# quadlets/heretek-blender-mcp.container — on-demand Blender MCP server (rootless Podman quadlet)
|
||||
#
|
||||
# Headless Blender FastMCP server from compose/profile-game.yml, expressed as
|
||||
# a rootless Podman systemd quadlet. StopWhenUnneeded=true keeps it at 0 MB
|
||||
# idle RAM until socket activation pulls it up. Image and loopback port mirror
|
||||
# the compose profile exactly: ghcr.io/sandraschi/blender-mcp:v1.2.0 on
|
||||
# 127.0.0.1:8781 (MIT-licensed FastMCP 3.2 stack, blender --background).
|
||||
# Socket-activated: heretek-blender-mcp-proxy.socket owns 127.0.0.1:8781. The
|
||||
# first connection starts heretek-blender-mcp-proxy.service (systemd-socket-proxyd),
|
||||
# which Requires= this unit, so the container is started on demand and proxied
|
||||
# to its container-only port 127.0.0.1:18781. When the proxy exits after
|
||||
# --exit-idle-time with no connections, nothing needs this unit any more and
|
||||
# StopWhenUnneeded=true stops the container: 0 MB RAM while idle. This unit has
|
||||
# no [Install] section on purpose — it must never start at login.
|
||||
#
|
||||
# Enable + start (user systemd):
|
||||
# Image: localhost/heretek/blender-mcp:0.11.1 (MIT FastMCP server + Blender
|
||||
# 4.2.3 LTS), the same pinned local build as compose/profile-game.yml. Build it
|
||||
# once before enabling the socket:
|
||||
# podman build -t localhost/heretek/blender-mcp:0.11.1 images/blender-mcp
|
||||
#
|
||||
# Install (see README "Quadlets"):
|
||||
# cp quadlets/heretek-blender-mcp.container ~/.config/containers/systemd/
|
||||
# cp quadlets/heretek-blender-mcp-proxy.socket quadlets/heretek-blender-mcp-proxy.service ~/.config/systemd/user/
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now heretek-blender-mcp.service
|
||||
# systemctl --user enable --now heretek-blender-mcp-proxy.socket
|
||||
#
|
||||
# SELinux: default container_t confinement (no SecurityLabelType override).
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Blender MCP background server (socket-activated, 0 MB idle RAM)
|
||||
Description=Heretek Blender MCP background server (socket-activated, stops when idle)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/sandraschi/blender-mcp:v1.2.0
|
||||
Image=localhost/heretek/blender-mcp:0.11.1
|
||||
Pull=never
|
||||
ContainerName=heretek-blender-mcp
|
||||
User=1000:1000
|
||||
PublishPort=127.0.0.1:8781:8781
|
||||
PublishPort=127.0.0.1:18781:8781
|
||||
Volume=heretek-blender-data:/data:Z
|
||||
DropCapability=ALL
|
||||
NoNewPrivileges=true
|
||||
SecurityLabelType=spc
|
||||
HealthCmd=bash -c 'exec 3<>/dev/tcp/127.0.0.1/8781'
|
||||
HealthInterval=10s
|
||||
HealthStartPeriod=60s
|
||||
Notify=healthy
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,15 @@
|
||||
# quadlets/heretek-ghidra-mcp-proxy.service — socket proxy for heretek-ghidra-mcp
|
||||
#
|
||||
# Started by heretek-ghidra-mcp-proxy.socket. Pulls up the container unit
|
||||
# (Requires=/After=; Notify=healthy there delays "started" until the server
|
||||
# answers) and forwards connections to the container's loopback port. It exits
|
||||
# after 15 idle minutes (--exit-idle-time, systemd-socket-proxyd's idle
|
||||
# timeout), which lets StopWhenUnneeded=true stop the container.
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Ghidra MCP socket proxy (exits when idle)
|
||||
Requires=heretek-ghidra-mcp.service heretek-ghidra-mcp-proxy.socket
|
||||
After=heretek-ghidra-mcp.service heretek-ghidra-mcp-proxy.socket
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time=15min 127.0.0.1:18089
|
||||
@@ -0,0 +1,14 @@
|
||||
# quadlets/heretek-ghidra-mcp-proxy.socket — activation socket for heretek-ghidra-mcp
|
||||
#
|
||||
# Listens on the loopback-only port compose also uses (127.0.0.1:8089) and
|
||||
# activates heretek-ghidra-mcp-proxy.service on the first connection.
|
||||
# This is the only Ghidra unit that is enabled (WantedBy=sockets.target).
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Ghidra MCP activation socket (127.0.0.1:8089)
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:8089
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -1,38 +1,52 @@
|
||||
# quadlets/heretek-ghidra-mcp.container — socket-activated Ghidra MCP server (Wave 3)
|
||||
# quadlets/heretek-ghidra-mcp.container — on-demand Ghidra MCP server (rootless Podman quadlet)
|
||||
#
|
||||
# Headless Ghidra MCP server from compose/profile-appsec.yml, expressed as a
|
||||
# rootless Podman systemd quadlet. StopWhenUnneeded=true keeps it at 0 MB idle
|
||||
# RAM until socket activation pulls it up. Image and loopback port mirror the
|
||||
# compose profile exactly: bethington/ghidra-mcp-headless:v6.0.0 on
|
||||
# 127.0.0.1:8089.
|
||||
# Socket-activated: heretek-ghidra-mcp-proxy.socket owns 127.0.0.1:8089. The
|
||||
# first connection starts heretek-ghidra-mcp-proxy.service (systemd-socket-proxyd),
|
||||
# which Requires= this unit, so the container is started on demand and proxied
|
||||
# to its container-only port 127.0.0.1:18089. When the proxy exits after
|
||||
# --exit-idle-time with no connections, nothing needs this unit any more and
|
||||
# StopWhenUnneeded=true stops the container: 0 MB RAM while idle. This unit has
|
||||
# no [Install] section on purpose — it must never start at login.
|
||||
#
|
||||
# Image: localhost/heretek/ghidra-mcp:6.0.0, the same pinned local build as
|
||||
# compose/profile-appsec.yml. Build it once before enabling the socket:
|
||||
# podman build -t localhost/heretek/ghidra-mcp:6.0.0 images/ghidra-mcp
|
||||
#
|
||||
# The mandatory bearer token is NOT stored here. Provide it via a user-owned
|
||||
# environment file before starting (fail closed — the server refuses
|
||||
# unauthenticated requests without it):
|
||||
# environment file (the container refuses to start without it):
|
||||
# printf 'GHIDRA_MCP_AUTH_TOKEN=<secret>\n' > ~/.config/heretek/ghidra-mcp.env
|
||||
# chmod 600 ~/.config/heretek/ghidra-mcp.env
|
||||
# systemctl --user enable --now heretek-ghidra-mcp.service
|
||||
#
|
||||
# Install (see README "Quadlets"):
|
||||
# cp quadlets/heretek-ghidra-mcp.container ~/.config/containers/systemd/
|
||||
# cp quadlets/heretek-ghidra-mcp-proxy.socket quadlets/heretek-ghidra-mcp-proxy.service ~/.config/systemd/user/
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now heretek-ghidra-mcp-proxy.socket
|
||||
#
|
||||
# SELinux: default container_t confinement (no SecurityLabelType override).
|
||||
|
||||
[Unit]
|
||||
Description=Heretek Ghidra MCP headless server (socket-activated, 0 MB idle RAM)
|
||||
Description=Heretek Ghidra MCP headless server (socket-activated, stops when idle)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Container]
|
||||
Image=bethington/ghidra-mcp-headless:v6.0.0
|
||||
Image=localhost/heretek/ghidra-mcp:6.0.0
|
||||
Pull=never
|
||||
ContainerName=heretek-ghidra-mcp
|
||||
User=1000:1000
|
||||
EnvironmentFile=%h/.config/heretek/ghidra-mcp.env
|
||||
PublishPort=127.0.0.1:8089:8089
|
||||
Environment=GHIDRA_MCP_ALLOWED_HOSTS=localhost,127.0.0.1
|
||||
PublishPort=127.0.0.1:18089:8089
|
||||
Volume=heretek-ghidra-data:/data:Z
|
||||
DropCapability=ALL
|
||||
NoNewPrivileges=true
|
||||
SecurityLabelType=spc
|
||||
HealthCmd=bash -c 'exec 3<>/dev/tcp/127.0.0.1/8089'
|
||||
HealthInterval=10s
|
||||
HealthStartPeriod=120s
|
||||
Notify=healthy
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
TimeoutStartSec=600
|
||||
Reference in new issue
Block a user