@dbx-tools/appkit-lakebox 0.0.0-stage → 0.9.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +104 -2
  3. package/index.ts +32 -0
  4. package/lib/index.d.ts +28 -0
  5. package/lib/index.js +26 -0
  6. package/lib/src/_package.d.ts +2 -0
  7. package/lib/src/_package.js +11 -0
  8. package/lib/src/_resource-registry.d.ts +41 -0
  9. package/lib/src/_resource-registry.js +128 -0
  10. package/lib/src/_service-snapshots.d.ts +55 -0
  11. package/lib/src/_service-snapshots.js +65 -0
  12. package/lib/src/config.d.ts +40 -0
  13. package/lib/src/config.js +86 -0
  14. package/lib/src/desktop.d.ts +6 -0
  15. package/lib/src/desktop.js +88 -0
  16. package/lib/src/metrics.d.ts +59 -0
  17. package/lib/src/metrics.js +151 -0
  18. package/lib/src/plugin.d.ts +259 -0
  19. package/lib/src/plugin.js +329 -0
  20. package/lib/src/routes.d.ts +75 -0
  21. package/lib/src/routes.js +441 -0
  22. package/lib/src/runtime.d.ts +154 -0
  23. package/lib/src/runtime.js +760 -0
  24. package/lib/src/services.d.ts +101 -0
  25. package/lib/src/services.js +188 -0
  26. package/lib/src/skills.d.ts +5 -0
  27. package/lib/src/skills.js +21 -0
  28. package/lib/src/user.d.ts +46 -0
  29. package/lib/src/user.js +76 -0
  30. package/lib/src/workspace.d.ts +43 -0
  31. package/lib/src/workspace.js +138 -0
  32. package/lib/tsconfig.tsbuildinfo +1 -0
  33. package/package.json +167 -4
  34. package/scripts/desktop-prerequisites.sh +66 -0
  35. package/scripts/desktop-session.sh +307 -0
  36. package/scripts/lakebox-admin.sh +234 -0
  37. package/scripts/lakebox-service.py +449 -0
  38. package/skills/html-page/SKILL.md +40 -0
  39. package/skills/jupyter-lab/SKILL.md +32 -0
  40. package/skills/lakebox-showcase/SKILL.md +66 -0
  41. package/skills/network-speed-test/SKILL.md +29 -0
  42. package/skills/realtime-whiteboard/SKILL.md +28 -0
  43. package/src/_package.ts +11 -0
  44. package/src/_resource-registry.ts +160 -0
  45. package/src/_service-snapshots.ts +84 -0
  46. package/src/config.ts +128 -0
  47. package/src/desktop.ts +97 -0
  48. package/src/metrics.ts +178 -0
  49. package/src/plugin.ts +446 -0
  50. package/src/routes.ts +524 -0
  51. package/src/runtime.ts +952 -0
  52. package/src/services.ts +239 -0
  53. package/src/skills.ts +23 -0
  54. package/src/user.ts +88 -0
  55. package/src/workspace.ts +185 -0
@@ -0,0 +1,307 @@
1
+ #!/bin/sh
2
+ # Desktop service entrypoint. Runs as the Lakebox user inside a
3
+ # host-environment crun container with a private /tmp, so the X socket is not
4
+ # visible to other Lakebox users. crun owns the process lifecycle.
5
+ set -eu
6
+ : "${DBX_TOOLS_DESKTOP_WALLPAPER_BASE64:?DBX_TOOLS_DESKTOP_WALLPAPER_BASE64 is required}"
7
+ : "${DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64:?DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64 is required}"
8
+ : "${DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64:?DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64 is required}"
9
+ : "${DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64:?DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64 is required}"
10
+ : "${DBX_TOOLS_DESKTOP_PRIMARY:?DBX_TOOLS_DESKTOP_PRIMARY is required}"
11
+ : "${DBX_TOOLS_DESKTOP_PRIMARY_HOVER:?DBX_TOOLS_DESKTOP_PRIMARY_HOVER is required}"
12
+ : "${DBX_TOOLS_DESKTOP_SURFACE:?DBX_TOOLS_DESKTOP_SURFACE is required}"
13
+ : "${DBX_TOOLS_DESKTOP_FONT:?DBX_TOOLS_DESKTOP_FONT is required}"
14
+ : "${SERVICE_PORT:?SERVICE_PORT is required}"
15
+ display=$((SERVICE_PORT - 20000 + 100))
16
+
17
+ runtime="$HOME/.dbx-tools/desktop"
18
+ session_script="$runtime/start-xfce.sh"
19
+ browser_script="$runtime/open-browser"
20
+ browser_homepage="$runtime/browser-homepage"
21
+ desktop_environment="$runtime/environment"
22
+ desktop_runner="$runtime/run"
23
+ log_file="$runtime/desktop.log"
24
+ wallpaper_svg="$runtime/dbx-tools-wallpaper.svg"
25
+ wallpaper_png="$runtime/dbx-tools-wallpaper.png"
26
+ brand_icon_svg="$runtime/dbx-tools-icon.svg"
27
+ brand_icon="$runtime/dbx-tools-icon.png"
28
+ terminal_icon_svg="$runtime/dbx-tools-terminal.svg"
29
+ terminal_icon="$runtime/dbx-tools-terminal.png"
30
+
31
+ mkdir -p "$runtime" "$HOME/.vnc" "$HOME/.config/gtk-3.0" \
32
+ "$HOME/.config/xfce4/xfconf/xfce-perchannel-xml" "$HOME/.config/xfce4" \
33
+ "$HOME/.local/share/applications" "$HOME/.local/share/xfce4/helpers" "$HOME/Desktop"
34
+
35
+ test -x "$(command -v kasmvncserver)" || {
36
+ printf 'kasmvncserver is unavailable after package setup.\n' >&2
37
+ exit 1
38
+ }
39
+ test -x "$(command -v kasmvncpasswd)" || {
40
+ printf 'kasmvncpasswd is unavailable after package setup.\n' >&2
41
+ exit 1
42
+ }
43
+ test -r /usr/share/kasmvnc/www/index.html || {
44
+ printf 'KasmVNC web client is unavailable after package setup.\n' >&2
45
+ exit 1
46
+ }
47
+
48
+ write_base64_asset() {
49
+ encoded="$1"
50
+ destination="$2"
51
+ temporary="$destination.tmp.$$"
52
+ printf '%s' "$encoded" | base64 -d >"$temporary"
53
+ if [ -f "$destination" ] && cmp -s "$temporary" "$destination"; then
54
+ rm -f "$temporary"
55
+ return 1
56
+ fi
57
+ mv "$temporary" "$destination"
58
+ return 0
59
+ }
60
+
61
+ if write_base64_asset "$DBX_TOOLS_DESKTOP_WALLPAPER_BASE64" "$wallpaper_svg" || \
62
+ [ ! -s "$wallpaper_png" ]; then
63
+ rsvg-convert --width 1600 --height 1000 "$wallpaper_svg" >"$wallpaper_png"
64
+ fi
65
+ if write_base64_asset "$DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64" "$brand_icon_svg" || \
66
+ [ ! -s "$brand_icon" ]; then
67
+ rsvg-convert --width 64 --height 64 "$brand_icon_svg" >"$brand_icon"
68
+ fi
69
+ if write_base64_asset "$DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64" "$terminal_icon_svg" || \
70
+ [ ! -s "$terminal_icon" ]; then
71
+ rsvg-convert --width 64 --height 64 "$terminal_icon_svg" >"$terminal_icon"
72
+ fi
73
+ write_base64_asset "$DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64" "$browser_homepage" || true
74
+
75
+ printf 'export DISPLAY=:%s\n' "$display" >"$desktop_environment"
76
+ chmod 600 "$desktop_environment"
77
+ cat >"$desktop_runner" <<'EOF'
78
+ #!/bin/sh
79
+ set -eu
80
+ . "$HOME/.dbx-tools/desktop/environment"
81
+ exec "$@"
82
+ EOF
83
+ chmod 700 "$desktop_runner"
84
+
85
+ cat >"$browser_script" <<'EOF'
86
+ #!/bin/sh
87
+ export GTK_A11Y=none
88
+ export WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS=1
89
+ if [ "$#" -eq 0 ]; then
90
+ set -- "$(cat "$HOME/.dbx-tools/desktop/browser-homepage")"
91
+ fi
92
+ exec epiphany "$@"
93
+ EOF
94
+ chmod 700 "$browser_script"
95
+
96
+ cat >"$HOME/.config/gtk-3.0/gtk.css" <<EOF
97
+ #XfcePanelWindow {
98
+ background-color: $DBX_TOOLS_DESKTOP_PRIMARY;
99
+ color: $DBX_TOOLS_DESKTOP_SURFACE;
100
+ }
101
+ #XfcePanelWindow button {
102
+ color: $DBX_TOOLS_DESKTOP_SURFACE;
103
+ border-color: transparent;
104
+ background-color: transparent;
105
+ border-radius: 8px;
106
+ margin: 3px;
107
+ }
108
+ #XfcePanelWindow button:hover,
109
+ #XfcePanelWindow button:checked {
110
+ background-color: $DBX_TOOLS_DESKTOP_PRIMARY_HOVER;
111
+ }
112
+ EOF
113
+
114
+ if [ -r /etc/xdg/xfce4/panel/default.xml ]; then
115
+ cp /etc/xdg/xfce4/panel/default.xml \
116
+ "$HOME/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml"
117
+ fi
118
+
119
+ cat >"$HOME/.config/mimeapps.list" <<'EOF'
120
+ [Default Applications]
121
+ text/html=dbx-tools-browser.desktop
122
+ x-scheme-handler/http=dbx-tools-browser.desktop
123
+ x-scheme-handler/https=dbx-tools-browser.desktop
124
+ EOF
125
+
126
+ cat >"$HOME/.local/share/applications/dbx-tools-browser.desktop" <<EOF
127
+ [Desktop Entry]
128
+ Type=Application
129
+ Name=dbx tools Browser
130
+ Comment=Browse the web from the Lakebox desktop
131
+ Exec=$browser_script %U
132
+ Icon=$brand_icon
133
+ Terminal=false
134
+ Categories=Network;WebBrowser;
135
+ MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
136
+ EOF
137
+
138
+ cat >"$HOME/.local/share/xfce4/helpers/dbx-tools-browser.desktop" <<EOF
139
+ [Desktop Entry]
140
+ Version=1.0
141
+ Icon=$brand_icon
142
+ Type=X-XFCE-Helper
143
+ Name=dbx tools Browser
144
+ StartupNotify=true
145
+ X-XFCE-Binaries=$browser_script;
146
+ X-XFCE-Category=WebBrowser
147
+ X-XFCE-Commands=$browser_script;
148
+ X-XFCE-CommandsWithParameter=$browser_script "%s";
149
+ EOF
150
+
151
+ cat >"$HOME/.config/xfce4/helpers.rc" <<'EOF'
152
+ WebBrowser=dbx-tools-browser
153
+ EOF
154
+ rm -f "$HOME/Desktop/Web Browser.desktop"
155
+
156
+ cat >"$HOME/.local/share/applications/dbx-tools-terminal.desktop" <<EOF
157
+ [Desktop Entry]
158
+ Type=Application
159
+ Name=Terminal
160
+ Comment=Open the Lakebox desktop terminal
161
+ Exec=xfce4-terminal
162
+ Icon=$terminal_icon
163
+ Terminal=false
164
+ Categories=System;TerminalEmulator;
165
+ EOF
166
+ chmod 700 "$HOME/.local/share/applications/dbx-tools-terminal.desktop"
167
+ rm -f "$HOME/Desktop/Terminal.desktop"
168
+
169
+ cat >"$HOME/.vnc/kasmvnc.yaml" <<EOF
170
+ desktop:
171
+ resolution:
172
+ width: 1600
173
+ height: 1000
174
+ allow_resize: true
175
+ pixel_depth: 24
176
+ network:
177
+ protocol: http
178
+ interface: 127.0.0.1
179
+ websocket_port: $SERVICE_PORT
180
+ use_ipv4: true
181
+ use_ipv6: false
182
+ ssl:
183
+ pem_certificate:
184
+ pem_key:
185
+ require_ssl: false
186
+ encoding:
187
+ max_frame_rate: 30
188
+ rect_encoding_mode:
189
+ min_quality: 7
190
+ max_quality: 9
191
+ consider_lossless_quality: 10
192
+ rectangle_compress_threads: auto
193
+ video_encoding_mode:
194
+ jpeg_quality: -1
195
+ webp_quality: -1
196
+ max_resolution:
197
+ width: 1920
198
+ height: 1080
199
+ enter_video_encoding_mode:
200
+ time_threshold: 1
201
+ area_threshold: 45%
202
+ exit_video_encoding_mode:
203
+ time_threshold: 2
204
+ scaling_algorithm: progressive_bilinear
205
+ webp_encoding_time: 30
206
+ video_streaming_mode:
207
+ codec: h264
208
+ server:
209
+ http:
210
+ headers:
211
+ - Cross-Origin-Embedder-Policy=require-corp
212
+ - Cross-Origin-Opener-Policy=same-origin
213
+ httpd_directory: /usr/share/kasmvnc/www
214
+ auto_shutdown:
215
+ no_user_session_timeout: never
216
+ active_user_session_timeout: never
217
+ inactive_user_session_timeout: never
218
+ command_line:
219
+ prompt: false
220
+ EOF
221
+
222
+ if [ ! -s "$HOME/.kasmpasswd" ]; then
223
+ kasm_password="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
224
+ printf '%s\n%s\n' "$kasm_password" "$kasm_password" | \
225
+ kasmvncpasswd -u dbx-tools -r -w "$HOME/.kasmpasswd" >/dev/null
226
+ fi
227
+
228
+ cat >"$session_script" <<EOF
229
+ #!/bin/sh
230
+ set -eu
231
+ runtime="\$HOME/.dbx-tools/desktop"
232
+ ready="\$runtime/session.ready"
233
+ log_file="\$runtime/desktop.log"
234
+ wallpaper="\$runtime/dbx-tools-wallpaper.png"
235
+ brand_icon="\$runtime/dbx-tools-icon.svg"
236
+ rm -f "\$ready"
237
+ if [ -z "\${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
238
+ exec dbus-run-session -- "\$0"
239
+ fi
240
+ startxfce4 >>"\$log_file" 2>&1 &
241
+ xfce_pid=\$!
242
+ for _ in \$(seq 1 160); do
243
+ if pgrep -x xfce4-panel >/dev/null 2>&1 && \
244
+ pgrep -x xfdesktop >/dev/null 2>&1 && pgrep -x xfwm4 >/dev/null 2>&1; then
245
+ set_xfconf() {
246
+ channel="\$1"
247
+ property="\$2"
248
+ type="\$3"
249
+ value="\$4"
250
+ if xfconf-query -c "\$channel" -p "\$property" >/dev/null 2>&1; then
251
+ xfconf-query -c "\$channel" -p "\$property" -s "\$value" >/dev/null 2>&1 || true
252
+ else
253
+ xfconf-query -c "\$channel" -p "\$property" --create -t "\$type" \
254
+ -s "\$value" >/dev/null 2>&1 || true
255
+ fi
256
+ }
257
+ xdg-mime default dbx-tools-browser.desktop text/html >/dev/null 2>&1 || true
258
+ xdg-mime default dbx-tools-browser.desktop x-scheme-handler/http >/dev/null 2>&1 || true
259
+ xdg-mime default dbx-tools-browser.desktop x-scheme-handler/https >/dev/null 2>&1 || true
260
+ xdg-settings set default-web-browser dbx-tools-browser.desktop >/dev/null 2>&1 || true
261
+ gsettings set org.gnome.Epiphany homepage-url \
262
+ "\$(cat "\$runtime/browser-homepage")" >/dev/null 2>&1 || true
263
+ gsettings set org.gnome.Epiphany ask-for-default false >/dev/null 2>&1 || true
264
+ set_xfconf xsettings /Net/ThemeName string Arc-Dark
265
+ set_xfconf xsettings /Net/IconThemeName string hicolor
266
+ set_xfconf xsettings /Gtk/FontName string '$DBX_TOOLS_DESKTOP_FONT 10'
267
+ set_xfconf xfwm4 /general/theme string Arc-Dark
268
+ set_xfconf xfce4-panel /panels/panel-1/size uint 36
269
+ set_xfconf xfce4-panel /plugins/plugin-1 string whiskermenu
270
+ set_xfconf xfce4-panel /plugins/plugin-1/button-icon string "\$brand_icon"
271
+ set_xfconf xfce4-panel /plugins/plugin-1/button-title string 'dbx tools'
272
+ set_xfconf xfce4-panel /plugins/plugin-1/show-button-title bool true
273
+ properties=\$(xfconf-query -c xfce4-desktop -l 2>/dev/null | \
274
+ grep -E '/(last-image|image-path)\$' || true)
275
+ if [ -z "\$properties" ]; then
276
+ properties='/backdrop/screen0/monitor0/workspace0/last-image
277
+ /backdrop/screen0/monitorVNC-0/workspace0/last-image
278
+ /backdrop/screen0/monitorDefault-0/workspace0/last-image'
279
+ fi
280
+ printf '%s\n' "\$properties" | while IFS= read -r property; do
281
+ [ -n "\$property" ] || continue
282
+ xfconf-query -c xfce4-desktop -p "\$property" --create -t string \
283
+ -s "\$wallpaper" >/dev/null 2>&1 || true
284
+ style=\$(printf '%s' "\$property" | sed -E 's/(last-image|image-path)\$/image-style/')
285
+ xfconf-query -c xfce4-desktop -p "\$style" --create -t int -s 5 >/dev/null 2>&1 || true
286
+ done
287
+ xfdesktop --reload >/dev/null 2>&1 || true
288
+ xfce4-panel --restart >/dev/null 2>&1 || true
289
+ touch "\$ready"
290
+ break
291
+ fi
292
+ kill -0 "\$xfce_pid" 2>/dev/null || exit 1
293
+ sleep 0.25
294
+ done
295
+ test -f "\$ready" || {
296
+ printf 'Xfce desktop shell did not become ready.\n' >&2
297
+ exit 1
298
+ }
299
+ wait "\$xfce_pid"
300
+ EOF
301
+ chmod 700 "$session_script"
302
+
303
+
304
+ # Each user's display number follows from the derived service port.
305
+ exec kasmvncserver ":$display" -fg -select-de xfce -xstartup "$session_script" \
306
+ -geometry 1600x1000 -depth 24 -interface 127.0.0.1 -websocketPort "$SERVICE_PORT" \
307
+ -disableBasicAuth -SecurityTypes None
@@ -0,0 +1,234 @@
1
+ #!/bin/sh
2
+ # Package-owned privileged Lakebox administration. Runs as root through the
3
+ # fixed bootstrap capability only; never receives REST, agent, or service input
4
+ # other than the validated hash, UID, digest, and port arguments below.
5
+ set -eu
6
+ umask 022
7
+
8
+ agent_home=/home/sandbox-agent
9
+ state=$agent_home/.dbx-tools
10
+ users=$state/users
11
+ identities=$state/identities
12
+ runtime=$state/runtime
13
+ bundle=$runtime/container-v1
14
+ run_root=/run/lakebox
15
+ port_min=20000
16
+ port_max=31999
17
+ agent_uid=$(id -u sandbox-agent)
18
+
19
+ valid_hash() {
20
+ case "$1" in
21
+ ''|*[!0-9a-v]*) return 1 ;;
22
+ esac
23
+ [ "${#1}" -eq 7 ]
24
+ }
25
+
26
+ valid_number() {
27
+ case "$1" in
28
+ ''|*[!0-9]*) return 1 ;;
29
+ esac
30
+ }
31
+
32
+ with_lock() {
33
+ install -d -m 0755 "$run_root"
34
+ exec 9>"$run_root/admin.lock"
35
+ flock 9
36
+ }
37
+
38
+ ensure_layout() {
39
+ chmod 0711 "$agent_home"
40
+ install -d -m 0711 -o root -g root "$state" "$users" "$runtime"
41
+ install -d -m 0700 -o root -g root "$identities"
42
+ install -d -m 0755 -o root -g root "$run_root" "$runtime/bin"
43
+ }
44
+
45
+ ensure_bundle() {
46
+ if [ -x "$bundle/root/usr/bin/buildah" ] && [ -x "$bundle/root/usr/bin/crun" ] &&
47
+ [ -f "$bundle/complete" ]; then
48
+ return 0
49
+ fi
50
+ echo "installing container runtime bundle"
51
+ debs=$(mktemp -d)
52
+ rm -rf "$bundle.tmp"
53
+ install -d -m 0755 "$bundle.tmp/root"
54
+ apt-get update -qq
55
+ DEBIAN_FRONTEND=noninteractive apt-get -qq -o Dir::Cache::archives="$debs" \
56
+ -o APT::Keep-Downloaded-Packages=true --download-only install -y \
57
+ --no-install-recommends buildah crun uidmap >/dev/null
58
+ for deb in "$debs"/*.deb; do dpkg-deb -x "$deb" "$bundle.tmp/root"; done
59
+ rm -rf "$debs"
60
+ chmod -R a+rX "$bundle.tmp"
61
+ touch "$bundle.tmp/complete"
62
+ rm -rf "$bundle"
63
+ mv "$bundle.tmp" "$bundle"
64
+ }
65
+
66
+ ensure_ttyd() {
67
+ version=1.7.7
68
+ case "$(uname -m)" in
69
+ aarch64) checksum=b38acadd89d1d396a0f5649aa52c539edbad07f4bc7348b27b4f4b7219dd4165 ;;
70
+ x86_64) checksum=8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55 ;;
71
+ *) echo "unsupported ttyd architecture $(uname -m)" >&2; return 1 ;;
72
+ esac
73
+ target=$runtime/bin/ttyd
74
+ if [ -x "$target" ] && printf '%s %s\n' "$checksum" "$target" | sha256sum --check --status; then
75
+ return 0
76
+ fi
77
+ echo "installing ttyd $version"
78
+ curl -fsSL -o "$target.tmp" \
79
+ "https://github.com/tsl0922/ttyd/releases/download/$version/ttyd.$(uname -m)"
80
+ printf '%s %s\n' "$checksum" "$target.tmp" | sha256sum --check --status
81
+ chmod 0755 "$target.tmp"
82
+ mv -f "$target.tmp" "$target"
83
+ }
84
+
85
+ # Installs one package-owned helper delivered as base64 when its content changed.
86
+ install_helper() {
87
+ name=$1
88
+ case "$name" in
89
+ lakebox-service|desktop-session|desktop-prerequisites) ;;
90
+ *) echo "unknown helper $name" >&2; return 2 ;;
91
+ esac
92
+ target=$runtime/bin/$name
93
+ temporary=$target.tmp.$$
94
+ printf %s "$2" | base64 -d >"$temporary"
95
+ if [ -f "$target" ] && cmp -s "$temporary" "$target"; then
96
+ rm -f "$temporary"
97
+ return 0
98
+ fi
99
+ chmod 0755 "$temporary"
100
+ mv -f "$temporary" "$target"
101
+ }
102
+
103
+ ensure_firewall() {
104
+ if nft list table inet lakebox >/dev/null 2>&1; then return 0; fi
105
+ nft -f - <<NFT
106
+ table inet lakebox {
107
+ set allowed {
108
+ typeof meta skuid . tcp dport
109
+ }
110
+ chain output {
111
+ type filter hook output priority 0; policy accept;
112
+ oifname "lo" tcp dport $port_min-$port_max meta skuid { 0, $agent_uid } accept
113
+ oifname "lo" tcp dport $port_min-$port_max meta skuid . tcp dport @allowed accept
114
+ oifname "lo" tcp dport $port_min-$port_max drop
115
+ }
116
+ }
117
+ NFT
118
+ }
119
+
120
+ # Recreates one account whose persistent home already exists. The UID is read
121
+ # from the persistent directory so file ownership survives every restart.
122
+ restore_account() {
123
+ hash=$1
124
+ persist=$users/$hash
125
+ name=lakebox-$hash
126
+ uid=$(stat -c %u "$persist")
127
+ existing=$(getent passwd "$uid" | cut -d: -f1 || true)
128
+ if [ -n "$existing" ] && [ "$existing" != "$name" ]; then
129
+ echo "UID $uid for $name is already used by $existing" >&2
130
+ return 1
131
+ fi
132
+ if ! getent group "$name" >/dev/null; then groupadd --gid "$uid" "$name"; fi
133
+ if ! getent passwd "$name" >/dev/null; then
134
+ useradd --uid "$uid" --gid "$uid" --no-create-home --home-dir "/home/$name" \
135
+ --shell /bin/bash "$name" 2>/dev/null
136
+ fi
137
+ passwd --lock "$name" >/dev/null 2>&1 || true
138
+ ln -sfn "$persist" "/home/$name"
139
+ install -d -m 0700 -o "$uid" -g "$uid" "$run_root/$hash"
140
+ for definition in "$persist"/.dbx-tools/services/*/service.json; do
141
+ [ -f "$definition" ] || continue
142
+ port=$(python3 -c 'import json, sys; print(int(json.load(open(sys.argv[1]))["port"]))' "$definition") || continue
143
+ allow_port "$uid" "$port"
144
+ done
145
+ }
146
+
147
+ allow_port() {
148
+ valid_number "$1" && valid_number "$2" || { echo "invalid port arguments" >&2; return 2; }
149
+ [ "$2" -ge "$port_min" ] && [ "$2" -le "$port_max" ] || {
150
+ echo "port $2 is outside the Lakebox service range" >&2
151
+ return 2
152
+ }
153
+ ensure_firewall
154
+ nft add element inet lakebox allowed "{ $1 . $2 }"
155
+ }
156
+
157
+ command=${1:-}
158
+ case "$command" in
159
+ bootstrap)
160
+ shift
161
+ with_lock
162
+ ensure_layout
163
+ for helper in "$@"; do
164
+ install_helper "${helper%%=*}" "${helper#*=}"
165
+ done
166
+ ensure_bundle
167
+ ensure_ttyd
168
+ ensure_firewall
169
+ for persist in "$users"/*; do
170
+ [ -d "$persist" ] || continue
171
+ hash=$(basename "$persist")
172
+ valid_hash "$hash" || continue
173
+ if restore_account "$hash"; then
174
+ echo "__LAKEBOX_USER__=$hash"
175
+ else
176
+ echo "skipped $hash" >&2
177
+ fi
178
+ done
179
+ echo "__LAKEBOX_BOOTSTRAP__=ready"
180
+ ;;
181
+ ensure-user)
182
+ hash=${2:-}
183
+ uid=${3:-}
184
+ digest=${4:-}
185
+ valid_hash "$hash" && valid_number "$uid" || { echo "invalid user arguments" >&2; exit 2; }
186
+ case "$digest" in ''|*[!0-9a-f]*) echo "invalid identity digest" >&2; exit 2 ;; esac
187
+ with_lock
188
+ ensure_layout
189
+ marker=$identities/$hash
190
+ if [ -f "$marker" ] && [ "$(cat "$marker")" != "$digest" ]; then
191
+ echo "Lakebox user hash $hash collides with another identity" >&2
192
+ exit 3
193
+ fi
194
+ persist=$users/$hash
195
+ if [ -d "$persist" ]; then
196
+ owner=$(stat -c %u "$persist")
197
+ if [ "$owner" != "$uid" ]; then
198
+ echo "Lakebox user $hash home is owned by UID $owner, expected $uid" >&2
199
+ exit 3
200
+ fi
201
+ else
202
+ existing=$(getent passwd "$uid" | cut -d: -f1 || true)
203
+ if [ -n "$existing" ] && [ "$existing" != "lakebox-$hash" ]; then
204
+ echo "Derived UID $uid for lakebox-$hash is already used by $existing" >&2
205
+ exit 3
206
+ fi
207
+ install -d -m 0700 -o "$uid" -g "$uid" "$persist"
208
+ fi
209
+ printf '%s\n' "$digest" >"$marker"
210
+ chmod 0600 "$marker"
211
+ restore_account "$hash"
212
+ ;;
213
+ allow-port)
214
+ allow_port "${2:-}" "${3:-}"
215
+ ;;
216
+ desktop-prerequisites)
217
+ with_lock
218
+ sh "$runtime/bin/desktop-prerequisites"
219
+ ;;
220
+ metrics)
221
+ # Machine-level counters only; never reads process tables.
222
+ printf 'cores %s\n' "$(nproc)"
223
+ head -n 1 /proc/stat
224
+ awk '{ print "load", $1 }' /proc/loadavg
225
+ awk '/^MemTotal:|^MemAvailable:/ { print $1, $2 }' /proc/meminfo
226
+ df -B1 --output=size,used "$agent_home" | awk 'NR == 2 { print "storage", $1, $2 }'
227
+ awk -F'[: ]+' 'NR > 2 && $2 != "lo" { rx += $3; tx += $11 }
228
+ END { print "network", rx + 0, tx + 0 }' /proc/net/dev
229
+ ;;
230
+ *)
231
+ echo "usage: lakebox-admin bootstrap [name=base64...]|ensure-user|allow-port|desktop-prerequisites|metrics" >&2
232
+ exit 2
233
+ ;;
234
+ esac