Repository: NetBSDfr/smolBSD Branch: main Commit: 8481574b7d2b Files: 139 Total size: 300.5 KB Directory structure: gitextract_hd2fc315/ ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── app/ │ ├── .flaskenv │ ├── README.md │ ├── app.py │ ├── index.html │ └── requirements.txt ├── bin/ │ └── .gitkeep ├── contribs/ │ ├── knockd.sh │ └── knockssh.sh ├── dockerfiles/ │ ├── Dockerfile.basic │ ├── Dockerfile.bsdshell │ ├── Dockerfile.caddy │ ├── Dockerfile.clawd │ ├── Dockerfile.crush │ ├── Dockerfile.dockinx │ └── Dockerfile.tiny ├── etc/ │ ├── base.conf │ ├── bozohttpd.conf │ ├── clawd.conf │ ├── games.conf │ ├── lhv-tools.conf │ ├── live.conf │ ├── nbakery.conf │ ├── nitrosshd.conf │ ├── rescue.conf │ ├── runbsd.conf │ ├── sshd.conf │ ├── systembsd.conf │ ├── tslog.conf │ └── usershell.conf ├── k8s/ │ ├── Dockerfile │ ├── README.md │ ├── generic-device-plugin.yaml │ └── smolbozo.yaml ├── misc/ │ └── vmbatch.md ├── mkimg.sh ├── mnt/ │ └── .gitkeep ├── scripts/ │ ├── app-run.sh │ ├── fetch.sh │ ├── freshchk.sh │ ├── sh │ └── uname.sh ├── service/ │ ├── base/ │ │ ├── etc/ │ │ │ └── rc │ │ └── postinst/ │ │ └── dostuff.sh │ ├── biosboot/ │ │ ├── README.md │ │ ├── etc/ │ │ │ └── rc │ │ └── options.mk │ ├── bozohttpd/ │ │ ├── etc/ │ │ │ └── rc │ │ └── postinst/ │ │ └── mkhtml.sh │ ├── bsdshell/ │ │ └── sailor.conf │ ├── build/ │ │ ├── etc/ │ │ │ ├── rc │ │ │ └── resolv.conf │ │ ├── options.mk │ │ └── postinst/ │ │ └── prepare.sh │ ├── clawd/ │ │ ├── LOCAL.md │ │ ├── README.md │ │ └── SCHIZOCLAW.md │ ├── common/ │ │ ├── basicrc │ │ ├── choupi │ │ ├── funcs │ │ ├── mount9p │ │ ├── pkgin │ │ ├── qemufwcfg │ │ ├── sailor.vars │ │ ├── shutdown │ │ └── vars │ ├── crush/ │ │ └── README.md │ ├── games/ │ │ ├── README.md │ │ └── etc/ │ │ └── rc │ ├── lhv-tools/ │ │ ├── README.md │ │ ├── etc/ │ │ │ └── rc │ │ ├── options.mk │ │ └── postinst/ │ │ └── postinstall.sh │ ├── mport/ │ │ └── etc/ │ │ └── rc │ ├── nbakery/ │ │ ├── etc/ │ │ │ ├── fstab │ │ │ ├── rc │ │ │ └── smol.ansi │ │ └── options.mk │ ├── nitro/ │ │ └── postinst/ │ │ └── 00-nitro.sh │ ├── nitrosshd/ │ │ ├── NETBSD_ONLY │ │ ├── README.md │ │ ├── options.mk │ │ └── postinst/ │ │ ├── 00-nitro.sh │ │ └── keygen.sh │ ├── pipe/ │ │ ├── etc/ │ │ │ └── rc │ │ ├── options.mk │ │ └── sailor.conf │ ├── rescue/ │ │ ├── etc/ │ │ │ └── rc │ │ └── options.mk │ ├── runbsd/ │ │ ├── etc/ │ │ │ ├── banner.ans │ │ │ ├── fstab │ │ │ ├── rc │ │ │ ├── rc.conf │ │ │ └── sysctl.conf │ │ └── postinst/ │ │ ├── 00-runit.sh │ │ ├── 01-sv.sh │ │ └── 02-tools.sh │ ├── sshd/ │ │ ├── README.md │ │ ├── etc/ │ │ │ └── rc │ │ ├── options.mk │ │ ├── postinst/ │ │ │ └── keygen.sh │ │ └── sailor.conf │ ├── systembsd/ │ │ ├── etc/ │ │ │ ├── banner.ans │ │ │ ├── dinit.d/ │ │ │ │ ├── boot │ │ │ │ ├── getty │ │ │ │ ├── loginready │ │ │ │ ├── motd │ │ │ │ ├── rc.boot │ │ │ │ ├── rc.boot.sh │ │ │ │ ├── rc.dev │ │ │ │ ├── rc.dev.sh │ │ │ │ ├── rc.fs │ │ │ │ ├── rc.fs.sh │ │ │ │ ├── sshd │ │ │ │ ├── syslogd │ │ │ │ └── wscons │ │ │ ├── fstab │ │ │ ├── rc │ │ │ ├── rc.conf │ │ │ ├── rc.local │ │ │ └── sysctl.conf │ │ └── postinst/ │ │ ├── 00-dinit.sh │ │ └── 01-custom.sh │ ├── tiny/ │ │ └── sailor.conf │ ├── tslog/ │ │ └── etc/ │ │ └── rc │ └── usershell/ │ ├── README.md │ ├── etc/ │ │ └── rc │ ├── options.mk │ ├── postinst/ │ │ └── custom.sh │ └── sailor.conf ├── smoler/ │ ├── build.sh │ └── img.sh ├── smoler.sh ├── startnb.sh └── www/ └── index.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/main.yml ================================================ name: Build smolBSD image on: push: branches: - main paths-ignore: - '**.md' - 'www/**' - 'app/**' workflow_dispatch: inputs: img: description: "Image target" required: true default: "base" arch: description: "Architecture" required: true default: "amd64" service: description: "Service to build on top of image" required: false default: "build" mountro: description: "Build as read-only (y or empty)" required: false default: "y" curlsh: description: "URL to a script to execute as finalizer" required: false default: jobs: build_img: runs-on: ubuntu-latest container: image: debian:latest options: --privileged steps: - name: Checkout uses: actions/checkout@v4 - name: Set up environment run: | apt update && apt install -y curl xz-utils make sudo git libarchive-tools rsync bmake e2fsprogs gdisk rm -rf /var/cache - name: Build image run: | for arch in amd64 evbarm-aarch64 do bmake SERVICE=${{ inputs.service }} CURLSH=${{ inputs.curlsh }} ARCH="$arch" MOUNTRO=${{ inputs.mountro || 'y' }} ${{ inputs.img || 'buildimg' }} # always build a small rescue image for debugging purposes bmake SERVICE=rescue ARCH="$arch" base done - name: Compress images run: | cd images for f in *.img; do echo "Compressing $f..." xz -T0 -9e "$f" sha256sum ${f}.xz > ${f}.xz.sha256 done rm -f *.img - name: Publish images to "latest" release uses: softprops/action-gh-release@v2 with: tag_name: latest name: "Latest smolBSD images (compressed)" files: | images/*.img.xz images/*.xz.sha256 prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ # editors /.idea # downloads & temporary files /tmp/ *.img /kernels/* /sets/** /images/* /pkgs/** /db/** netbsd-* # app build & env /app/bin/ /app/lib/ /app/pyvenv.cfg /sailor/ # personal keys /service/sshd/etc/*.pub /service/nitrosshd/etc/*.pub ================================================ FILE: LICENSE ================================================ Copyright 2023 Emile `iMil' Heitor Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Makefile ================================================ ARCH!= ARCH=${ARCH} scripts/uname.sh -m MACHINE!= scripts/uname.sh -p OS!= uname -s SETSEXT?= tar.xz .if ${ARCH} == "evbarm-aarch64" KERNEL= netbsd-GENERIC64.img LIVEIMGGZ= https://nycdn.netbsd.org/pub/NetBSD-daily/HEAD/latest/evbarm-aarch64/binary/gzimg/arm64.img.gz .elif ${ARCH} == "i386" KERNEL= netbsd-SMOL386 KDIST= https://smolbsd.org/assets SETSEXT= tgz .else KERNEL= netbsd-SMOL KDIST= https://smolbsd.org/assets LIVEIMGGZ= https://nycdn.netbsd.org/pub/NetBSD-daily/HEAD/latest/images/NetBSD-11.99.5-amd64-live.img.gz .endif .-include "service/${SERVICE}/options.mk" .-include "service/${SERVICE}/own.mk" VERS?= 11 PKGVERS?= 11.0 # for an obscure reason, packages path use uname -p... DIST?= https://nycdn.netbsd.org/pub/NetBSD-daily/netbsd-${VERS}/latest/${ARCH}/binary PKGSITE?= https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/${MACHINE}/${PKGVERS}/All KDIST?= ${DIST} WHOAMI!= whoami USER!= id -un GROUP!= id -gn BUILDIMG= build-${ARCH}.img BUILDIMGPATH= images/${BUILDIMG} BUILDIMGURL= https://github.com/NetBSDfr/smolBSD/releases/download/latest/${BUILDIMG} BUILDCPUS?= 2 BUILDMEM?= 1024 # used to set console in boot.cfg for BIOS boot BIOSCONSOLE?= com0 SERVICE?= ${.TARGET} # guest root filesystem will be read-only .if defined(MOUNTRO) && ${MOUNTRO} == "y" EXTRAS+= -o .endif # enable BIOS boot .if defined(BIOSBOOT) && ${BIOSBOOT} == "y" BIOSKERNEL?= kernels/netbsd-GENERIC EXTRAS+= -b -k ${BIOSKERNEL} .endif SETSDIR= sets/${ARCH} PKGSDIR= pkgs/${ARCH} LIVEIMG= images/NetBSD-${ARCH}-live.img # sets to fetch, defaults to base SETS?= base.${SETSEXT} etc.${SETSEXT} # Default: BSD DDUNIT= m CKSUM= cksum -a sha256 -c CKSUMQ= -q .if ${OS} == "Linux" DDUNIT= M CKSUM= sha256sum -c CKSUMQ= --quiet .elif ${OS} == "Darwin" CKSUM= shasum -a 256 -c .endif FETCH= scripts/fetch.sh FRESHCHK= scripts/freshchk.sh # extra remote script .if defined(CURLSH) && !empty(CURLSH) EXTRAS+= -c ${CURLSH} .endif # default memory amount for a guest MEM?= 256 # default port redirect, gives network to the guest PORT?= ::22022-:22 IMGSIZE?= 512 IMGNAME?= ${SERVICE}-${ARCH}${IMGTAG}.img DSTIMG?= images/${IMGNAME} # variables to transfer to mkimg.sh ENVVARS= SERVICE=${SERVICE} \ ARCH=${ARCH} \ PKGVERS=${PKGVERS} \ MOUNTRO=${MOUNTRO} \ BIOSBOOT=${BIOSBOOT} \ PKGSITE=${PKGSITE} \ ADDPKGS="${ADDPKGS}" \ MINIMIZE=${MINIMIZE} \ BIOSCONSOLE=${BIOSCONSOLE} \ FROMIMG=${FROMIMG} .if ${WHOAMI} != "root" && !defined(NOSUDO) # allow non root builds SUDO!= command -v doas >/dev/null && \ echo '${ENVVARS} doas' || \ echo 'sudo -E ${ENVVARS}' .else SUDO= ${ENVVARS} .endif # QUIET: default to quiet mode with Q=@, use Q= for verbose Q=@ CHOUPI= ./service/common/choupi ARROW!= . ${CHOUPI} && echo "$$ARROW" CHECK!= . ${CHOUPI} && echo "$$CHECK" CPU!= . ${CHOUPI} && echo "$$CPU" FREEZE!= . ${CHOUPI} && echo "$$FREEZE" WHITEBULLET!= . ${CHOUPI} && echo "$$WHITEBULLET" help: # This help you are reading $Qgrep '^[a-z]\+:.*#' Makefile kernfetch: $Qif [ "${ARCH}" = "amd64" ] || [ "${ARCH}" = "i386" ]; then \ ${FRESHCHK} ${KDIST}/${KERNEL} kernels/${KERNEL} || \ ${FETCH} -o kernels/${KERNEL} ${KDIST}/${KERNEL}; \ cd kernels && curl -L -s -o- ${KDIST}/${KERNEL}.sha256 | \ ${CKSUM} ${CKSUMQ} && \ echo "${CHECK} ${KERNEL} sha256 checks out"; \ else \ ${FRESHCHK} ${KDIST}/kernel/${KERNEL}.gz kernels/${KERNEL} || \ curl -L -o- ${KDIST}/kernel/${KERNEL}.gz | \ gzip -dc > kernels/${KERNEL}; \ fi setfetch: @[ -d ${SETSDIR} ] || mkdir -p ${SETSDIR} $Qfor s in ${SETS}; do \ ${FRESHCHK} ${DIST}/sets/$${s%:*} ${SETSDIR}/$${s%:*} || \ ${FETCH} -o ${SETSDIR}/$${s} ${DIST}/sets/$${s}; \ done pkgfetch: @[ -d ${PKGSDIR} ] || mkdir -p ${PKGSDIR} $Qfor p in ${ADDPKGS};do \ ${FRESHCHK} ${PKGSITE}/$${p}* ${PKGSDIR}/$${p}.tgz || \ ${FETCH} -o ${PKGSDIR}/$${p}.tgz ${PKGSITE}/$${p}*; \ done fetchall: kernfetch setfetch pkgfetch base: $Qecho "${CPU} destination architecture: ${ARCH} / ${MACHINE}" # if we are on the builder vm, don't fetchall again $Q[ -f tmp/build-${SERVICE} ] || ${MAKE} fetchall $Qecho "${ARROW} creating root filesystem (${IMGSIZE}M)" $Q${SUDO} ./mkimg.sh -i ${DSTIMG} -s ${SERVICE} \ -m ${IMGSIZE} -x "${SETS}" ${EXTRAS} $Q${SUDO} chown ${USER}:${GROUP} ${DSTIMG} $Qif [ -n "${MOUNTRO}" ]; then \ echo "${FREEZE} system root filesystem will be read-only"; fi $Qecho "${CHECK} image ready: ${DSTIMG}" buildimg: $Qecho "${ARROW} building the builder image" $Qrm -f tmp/* $Q${MAKE} SERVICE=build IMGTAG= base fetchimg: $Qecho "${ARROW} fetching builder image" $Q${FRESHCHK} ${BUILDIMGURL}.xz || \ curl -L -o- ${BUILDIMGURL}.xz | xz -dc > ${BUILDIMGPATH} build: fetchall # Build an image (with SERVICE=$SERVICE from service/) $Qif [ ! -f ${BUILDIMGPATH} ]; then \ if [ "${OS}" = "NetBSD" ] || [ "${OS}" = "Linux" ]; then \ ${MAKE} buildimg; \ else \ ${MAKE} fetchimg; \ fi; \ fi $Qmkdir -p tmp # wipe any leftover from possibly cancelled previous run # tmp holds: # * ENVVARS # * Dockerfile generated options # * optional new size for resize $Qrm -f tmp/* # save variables for sourcing in the build vm $Qecho "${ENVVARS}" | \ sed -E 's/[[:blank:]]+([A-Z_]+)/\n\1/g;s/=[[:blank:]]*([[:print:]]+)/="\1"/g' > \ tmp/build-${SERVICE} # build args from Dockefile $Qif [ -n "${BUILDARGS}" ]; then \ printf '%s\n' "${BUILDARGS}" | tr ',' '\n' >>tmp/build-${SERVICE}; \ fi # image tag for OCI images $Qecho "IMGTAG=${IMGTAG}" >>tmp/build-${SERVICE} $Qecho "${ARROW} creating the disk image" # generate disk image on the host, faster and avoids layering on 9p $Qdd if=/dev/zero of=${DSTIMG} bs=1${DDUNIT} count=${IMGSIZE} $Qecho "${ARROW} starting the builder microvm with" $Qecho " ${WHITEBULLET} ${BUILDMEM}MB RAM" $Qecho " ${WHITEBULLET} ${BUILDCPUS} cores" # Fire up the builder microVM $Q./startnb.sh -k kernels/${KERNEL} -i ${BUILDIMGPATH} -l ${DSTIMG} \ -c ${BUILDCPUS} -m ${BUILDMEM} \ -p ${PORT} -w . -x "-pidfile qemu-${.TARGET}.pid" & # wait till the build is finished, guest removes the lock $Qwhile [ -f tmp/build-${SERVICE} ]; do sleep 0.2; done $Qecho "${ARROW} killing the builder microvm" $Qkill $$(cat qemu-${.TARGET}.pid) $Qif [ -n "${MINIMIZE}" ] && [ -f "tmp/${IMGNAME}.size" ]; then \ while lsof ${DSTIMG} >/dev/null 2>&1; do sleep 0.2; done; \ qemu-img resize -q -f raw --shrink ${DSTIMG} \ $$(cat tmp/${IMGNAME}.size); \ fi # poor man's sig $Qecho "smolsig:$$(date +%d/%m/%Y)|$$(uuidgen)" | \ tee -a ${DSTIMG} >${DSTIMG:S/.img/.sig/} $Q${SUDO} chown ${USER}:${GROUP} ${DSTIMG} # cleanup metadata $Qrm -f tmp/* rescue: # Build a rescue image ${MAKE} SERVICE=rescue build live: kernfetch # Build a live image $Qecho "fetching ${LIVEIMG}" [ -f ${LIVEIMG} ] || curl -L -o- ${LIVEIMGGZ}|gzip -dc > ${LIVEIMG} ================================================ FILE: README.md ================================================
**smolBSD**
build your own minimal BSD UNIX system



## App Usage
## QuickStart
~~~
scripts/app-run.sh # just does Setup, Config & Run
firefox http://localhost:5000
~~~
## Setup
~~~
cd app/
python3 -m venv .
. bin/activate
pip install -r requirements.txt
~~~
## Running
~~~
cd app/
. bin/activate
flask run
~~~
## Configuration
~~~
- 1st, env vars have preference
- 2nd, app vars at .env
- 3rd, flask vars at .flaskenv
~~~
## Cleanup
~~~
rm -ri bin/ include/ lib/ lib64/ __pycache__/ pyvenv.cfg
~~~
================================================
FILE: app/app.py
================================================
import json
import logging
import os
import psutil
import socket
import subprocess
import dotenv
import sys
from flask import Flask, send_file, jsonify, request
app = Flask(__name__)
# Get environment variables from .flaskenv / .env
dotenv.load_dotenv()
cwd = os.environ['FLASK_CWD'] if 'FLASK_CWD' in os.environ else '..'
loglevel = int(os.environ['FLASK_LOGLEVEL']) if 'FLASK_LOGLEVEL' in os.environ else logging.ERROR
log = logging.getLogger('werkzeug')
log.setLevel(loglevel)
vmlist = {}
def get_vmlist():
vmlist.clear()
for filename in os.listdir(f'{cwd}/etc'):
if filename.endswith('.conf'):
vmname = filename.split(".conf")[0]
config_data = {}
with open(f'{cwd}/etc/{filename}', 'r') as f:
lines = f.readlines()
for line in lines:
line = line.strip()
if not line:
continue
elif line.startswith('#') or line.startswith('extra'):
continue
elif '=' in line:
key, value = line.split('=', 1)
config_data[key.strip()] = value.strip()
# Check if QEMU process is running
status = 'running' if get_pid(vmname) > 0 else 'stopped'
vmlist[vmname] = config_data
vmlist[vmname]['status'] = status
return vmlist
def list_files(path):
try:
items = [f for f in os.listdir(path) if not f.startswith(".")]
return jsonify(items)
except FileNotFoundError:
return jsonify({"error": "Directory not found"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
def get_port(vmname, service, default_port):
port = default_port
if vmname in vmlist and service in vmlist[vmname]:
return vmlist[vmname][service]
for vm in vmlist:
if not service in vmlist[vm]:
continue
vm_port = int(vmlist[vm][service])
if vm_port >= port:
port = vm_port + 1
return port
def query_qmp(command, vmname):
if not 'qmp_port' in vmlist[vmname]:
return jsonify(
{"success": False, "message": f"QMP not enabled in {vmname}"}
), 404
qmp_port = int(vmlist[vmname]['qmp_port'])
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
rep_len = 8192
s.connect(("localhost", qmp_port))
response = s.recv(rep_len)
# mandatory before command (duh?)
s.sendall('{"execute": "qmp_capabilities"}\n'.encode('utf-8'))
response = s.recv(rep_len)
# actual command
s.sendall(f'{{"execute": "{command}"}}\n'.encode('utf-8'))
response = s.recv(rep_len)
return json.loads(response)
def get_pid(vmname):
pid_file = f"{cwd}/qemu-{vmname}.pid"
if not os.path.exists(pid_file):
return -1
f = open(pid_file, "r")
pid = int(f.read().strip())
if psutil.pid_exists(pid):
return pid
else:
return -1
def get_cpu_usage(vmname):
ncpus = 1
r = query_qmp("query-cpus-fast", vmname)
if r and 'return' in r:
ncpus = len(r['return'])
pid = get_pid(vmname)
if pid < 0:
return 0
process = psutil.Process(pid)
return process.cpu_percent(interval=0.1) / ncpus
## routes
@app.route("/")
def index():
# do not render template, frontend logic handled by Vue
return send_file("index.html")
@app.route("/static/smolBSD.png")
def assets():
return send_file("static/smolBSD.png")
@app.route("/vmlist")
def vm_list():
return jsonify(get_vmlist())
@app.route("/getkernels")
def getkernels():
return list_files(f'{cwd}/kernels/')
@app.route("/getimages")
def getimages():
return list_files(f'{cwd}/images/')
@app.route("/start", methods=["POST"])
def start_vm():
vm_name = request.json.get("vm_name")
config_file = f'{cwd}/etc/{vm_name}.conf'
if not os.path.exists(config_file):
return jsonify(
{"success": False, "message": "Config file not found"}
), 404
try:
ret = subprocess.Popen([f"{cwd}/startnb.sh", "-f", config_file, "-d"], cwd=cwd)
return jsonify({"success": True, "message": "Starting VM"})
except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
@app.route("/stop", methods=["POST"])
def stop_vm():
vm_name = request.json.get("vm_name")
pid_file = f'{cwd}/qemu-{vm_name}.pid'
try:
pid = get_pid(vm_name)
if pid < 0:
return jsonify(
{"success": False, "message": "PID file not found"}
), 404
os.kill(int(pid), 15)
os.remove(pid_file)
return jsonify({"success": True, "message": "Stopping VM"})
except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
@app.route("/saveconf", methods=['POST'])
def saveconf():
try:
data = request.get_json()
if not data:
return jsonify(
{"success": False, "message": "No JSON payload provided"}
), 400
vmname = data.get("vm")
if not vmname:
return jsonify(
{
"success": False,
"message": "'vm' key is required in the JSON payload"
}
), 400
os.makedirs(f'{cwd}/etc', exist_ok=True)
file_path = f"{cwd}/etc/{vmname}.conf"
with open(file_path, 'w') as file:
for key, value in data.items():
if key == "tcpserial":
if value is True:
serial_port = get_port(vmname, 'serial_port', 5555)
key = "serial_port"
value = f"{serial_port}"
else:
continue
if key == "rmprotect" and value == False:
continue
file.write(f"{key}={value}\n")
qmp_port = get_port(vmname, 'qmp_port', 4444)
file.write(f'qmp_port={qmp_port}\n')
extra = 'extra="-pidfile qemu-${vm}.pid"'
file.write(f'{extra}\n')
return jsonify({"success": True, "message": file_path}), 200
except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
@app.route('/rm/
# smolClaw
[picoclaw][1] running on a microVM!
build your own minimal BSD UNIX system
smolBSD is a meta-operating system built on top of NetBSD. It lets you compose your own UNIX environment — from a single-purpose microservice system to a fully-custom OS image — in just a few minutes.
The smolBSD environment uses the netbsd-MICROVM kernel as its foundation, leveraging the same portable, reliable codebase that powers NetBSD itself. You decide what to include — sshd, httpd, or your own service — and smolBSD builds a coherent, minimal, bootable image ready to run anywhere.
$ bmake SERVICE=bozohttpd build
➡️ starting the builder microvm
➡️ host filesystem mounted on /mnt
➡️ fetching sets
➡️ creating root filesystem (512M)
✅ image ready: bozohttpd-amd64.img
Build BSD systems like you build software — fast, reproducible, and minimal.
Pick only the components you need — from kernel to services.
Every build is deterministic, portable, and easy to version-control.
Powered by netbsd-MICROVM — boot to service in milliseconds.
Runs anywhere QEMU or Firecracker runs — cloud, CI, edge, or laptop.
Build and boot your own BSD system in seconds:
$ git clone https://github.com/NetBSDfr/smolBSD
$ cd smolBSD
$ cat ~/.ssh/id_ed25519.pub >> service/sshd/etc/mykey.pub
$ bmake SERVICE=sshd build
➡️ starting the builder microvm
➡️ fetching sets
➡️ creating root filesystem (512M)
✅ image ready: sshd-amd64.img
➡️ killing the builder microvm
$ ./startnb.sh -f etc/sshd.conf
[ 1.0092096] kernel boot time: 14ms
Starting sshd.
Server listening on :: port 22.
Server listening on 0.0.0.0 port 22.
$ ssh -p 2022 ssh@localhost
Download
Your own service in seconds using a simple Dockerfile!
FROM base,etc
LABEL SERVICE=caddy
RUN pkgin up && pkgin -y in caddy
EXPOSE 8881:8880
CMD caddy respond -l :8880
A complete static web server in a few megabytes.
smolBSD builds a minimal system with bozohttpd
preconfigured and ready to serve content immediately on boot.
$ bmake SERVICE=bozohttpd build
➡️ starting the builder microvm
➡️ fetching sets
➡️ creating root filesystem (512M)
✅ image ready: bozohttpd-amd64.img
$ ./startnb.sh -f etc/bozohttpd.conf
[ 1.001231] kernel boot time: 10ms
Starting bozohttpd on :80
listening on 0.0.0.0:80
A lightweight build and image creation service based entirely on
NetBSD tools.
The nbakery image gives you a taste of a preconfigured NetBSD environment with all the well known tools.
$ bmake SERVICE=nbakery build
➡️ starting the builder microvm
➡️ fetching sets
➡️ creating root filesystem (512M)
✅ image ready: nbakery-amd64.img
$ ./startnb.sh -f etc/nbakery.conf
[ 1.008374] kernel boot time: 11ms
Welcome to the (n)bakery! 🧁
💪 doas <command> to run command as root
📦 pkgin to manage packages
🚪 exit to cleanly shutdown, ^a-x to exit qemu
🪟 you are inside a tmux with prefix ^q
A minimal secure shell server started with nitro,
designed to launch instantly and provide remote access with zero unnecessary
services.
Ideal for an SSH bouncer.
$ bmake SERVICE=nitrosshd build
➡️ starting the builder microvm
➡️ fetching sets
➡️ creating root filesystem (512M)
✅ image ready: nitrosshd-amd64.img
$ ./startnb.sh -f etc/sshd.conf
[ 1.011598] kernel boot time: 12ms
Created tmpfs /dev (1835008 byte, 3552 inodes)
Starting sshd.
Server listening on :: port 22.
Server listening on 0.0.0.0 port 22.
smolBSD is an independent project built on top of NetBSD. Join us, share your micro-systems, or contribute new services and build recipes.