Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
efd90d3
refactor: add delete_dir utility and use across codebase
dr-bonez Mar 9, 2026
95a519c
feat: improve service version migration and data version handling
dr-bonez Mar 9, 2026
ba71f20
fix: mark private domain hostnames as non-public
dr-bonez Mar 9, 2026
68ae365
feat: add bridge filter kind to service interface
dr-bonez Mar 9, 2026
ea8a7c0
feat: add s9pk inspect commitment subcommand
dr-bonez Mar 9, 2026
5316d6e
chore: update patch-db submodule
dr-bonez Mar 9, 2026
f56262b
chore: remove --unhandled-rejections=warn from container-runtime
dr-bonez Mar 9, 2026
36bf55c
chore: restructure release signatures into subdirectory
dr-bonez Mar 9, 2026
8ef4ef4
fix: add ca-certificates dependency to registry-deb
dr-bonez Mar 9, 2026
43e514f
fix: improve NVIDIA driver build in image recipe
dr-bonez Mar 9, 2026
c52fcf5
feat: add DbWatchedCallbacks abstraction, TypedDbWatch-based callback…
dr-bonez Mar 9, 2026
76de6be
refactor: extract Watchable<T> base class for SDK effect wrappers
dr-bonez Mar 9, 2026
7b05a7c
fix: add network dependency to start-tunneld and rename web reset to …
dr-bonez Mar 10, 2026
3441d4d
fix: update patch-db (ciborium revert) and create /media/startos as 750
dr-bonez Mar 10, 2026
9546fc9
fix: make GRUB serial console conditional on hardware availability
dr-bonez Mar 10, 2026
ccf6fa3
fix: set correct binary name and version on all CLI commands
dr-bonez Mar 10, 2026
2586f84
fix: make unmount idempotent by ignoring "not mounted" errors
dr-bonez Mar 10, 2026
73c6696
refactor: simplify AddPackageSignerParams merge field from Option<boo…
dr-bonez Mar 10, 2026
8dd50eb
fix: move unpack progress completion after rename and reformat
dr-bonez Mar 10, 2026
d2f12a7
fix: run apt-get update before installing registry deb in Docker image
dr-bonez Mar 10, 2026
36b8fda
fix: gracefully handle mount failure in legacy dependenciesAutoconfig
dr-bonez Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/start-registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ jobs:

ADD *.deb .

RUN apt-get install -y ./*_$(uname -m).deb && rm *.deb
RUN apt-get update && apt-get install -y ./*_$(uname -m).deb && rm -rf *.deb /var/lib/apt/lists/*

VOLUME /var/lib/startos

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ results/$(BASENAME).deb: debian/dpkg-build.sh $(call ls-files,debian/startos) $(
registry-deb: results/$(REGISTRY_BASENAME).deb

results/$(REGISTRY_BASENAME).deb: debian/dpkg-build.sh $(call ls-files,debian/start-registry) $(REGISTRY_TARGETS)
PROJECT=start-registry PLATFORM=$(ARCH) REQUIRES=debian ./build/os-compat/run-compat.sh ./debian/dpkg-build.sh
PROJECT=start-registry PLATFORM=$(ARCH) REQUIRES=debian DEPENDS=ca-certificates ./build/os-compat/run-compat.sh ./debian/dpkg-build.sh

tunnel-deb: results/$(TUNNEL_BASENAME).deb

Expand Down
20 changes: 15 additions & 5 deletions build/image-recipe/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ cat > config/hooks/normal/9000-install-startos.hook.chroot << EOF

set -e

if [ "${IB_TARGET_PLATFORM}" != "raspberrypi" ]; then
/usr/lib/startos/scripts/enable-kiosk
fi

if [ "${NVIDIA}" = "1" ]; then
# install a specific NVIDIA driver version

Expand Down Expand Up @@ -236,7 +240,7 @@ if [ "${NVIDIA}" = "1" ]; then
echo "[nvidia-hook] Target kernel version: \${KVER}" >&2

# Ensure kernel headers are present
TEMP_APT_DEPS=(build-essential)
TEMP_APT_DEPS=(build-essential pkg-config)
if [ ! -e "/lib/modules/\${KVER}/build" ]; then
TEMP_APT_DEPS+=(linux-headers-\${KVER})
fi
Expand Down Expand Up @@ -279,6 +283,16 @@ if [ "${NVIDIA}" = "1" ]; then

echo "[nvidia-hook] NVIDIA \${NVIDIA_DRIVER_VERSION} installation complete for kernel \${KVER}" >&2

echo "[nvidia-hook] Removing .run installer..." >&2
rm -f "\${RUN_PATH}"

echo "[nvidia-hook] Blacklisting nouveau..." >&2
echo "blacklist nouveau" > /etc/modprobe.d/blacklist-nouveau.conf
echo "options nouveau modeset=0" >> /etc/modprobe.d/blacklist-nouveau.conf

echo "[nvidia-hook] Rebuilding initramfs..." >&2
update-initramfs -u -k "\${KVER}"

echo "[nvidia-hook] Removing build dependencies..." >&2
apt-get purge -y nvidia-depends
apt-get autoremove -y
Expand Down Expand Up @@ -310,10 +324,6 @@ usermod -aG systemd-journal start9

echo "start9 ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/010_start9-nopasswd"

if [ "${IB_TARGET_PLATFORM}" != "raspberrypi" ]; then
/usr/lib/startos/scripts/enable-kiosk
fi

if ! [[ "${IB_OS_ENV}" =~ (^|-)dev($|-) ]]; then
passwd -l start9
fi
Expand Down
1 change: 1 addition & 0 deletions build/lib/scripts/startos-initramfs-module
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ local_mount_root()
-olowerdir=/startos/config/overlay:/lower,upperdir=/upper/data,workdir=/upper/work \
overlay ${rootmnt}

mkdir -m 750 -p ${rootmnt}/media/startos
mkdir -p ${rootmnt}/media/startos/config
mount --bind /startos/config ${rootmnt}/media/startos/config
mkdir -p ${rootmnt}/media/startos/images
Expand Down
21 changes: 12 additions & 9 deletions build/manage-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -198,20 +198,22 @@ cmd_sign() {
enter_release_dir
resolve_gh_user

mkdir -p signatures

for file in $(release_files); do
gpg -u $START9_GPG_KEY --detach-sign --armor -o "${file}.start9.asc" "$file"
gpg -u $START9_GPG_KEY --detach-sign --armor -o "signatures/${file}.start9.asc" "$file"
if [ -n "$GH_USER" ] && [ -n "$GH_GPG_KEY" ]; then
gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "${file}.${GH_USER}.asc" "$file"
gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "signatures/${file}.${GH_USER}.asc" "$file"
fi
done

gpg --export -a $START9_GPG_KEY > start9.key.asc
gpg --export -a $START9_GPG_KEY > signatures/start9.key.asc
if [ -n "$GH_USER" ] && [ -n "$GH_GPG_KEY" ]; then
gpg --export -a "$GH_GPG_KEY" > "${GH_USER}.key.asc"
gpg --export -a "$GH_GPG_KEY" > "signatures/${GH_USER}.key.asc"
else
>&2 echo 'Warning: could not determine GitHub user or GPG signing key, skipping personal signature'
fi
tar -czvf signatures.tar.gz *.asc
tar -czvf signatures.tar.gz -C signatures .

gh release upload -R $REPO "v$VERSION" signatures.tar.gz --clobber
}
Expand All @@ -229,17 +231,18 @@ cmd_cosign() {

echo "Downloading existing signatures..."
gh release download -R $REPO "v$VERSION" -p "signatures.tar.gz" -D "$(pwd)" --clobber
tar -xzf signatures.tar.gz
mkdir -p signatures
tar -xzf signatures.tar.gz -C signatures

echo "Adding personal signatures as $GH_USER..."
for file in $(release_files); do
gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "${file}.${GH_USER}.asc" "$file"
gpg -u "$GH_GPG_KEY" --detach-sign --armor -o "signatures/${file}.${GH_USER}.asc" "$file"
done

gpg --export -a "$GH_GPG_KEY" > "${GH_USER}.key.asc"
gpg --export -a "$GH_GPG_KEY" > "signatures/${GH_USER}.key.asc"

echo "Re-packing signatures..."
tar -czvf signatures.tar.gz *.asc
tar -czvf signatures.tar.gz -C signatures .

gh release upload -R $REPO "v$VERSION" signatures.tar.gz --clobber
echo "Done. Personal signatures for $GH_USER added to v$VERSION."
Expand Down
2 changes: 1 addition & 1 deletion container-runtime/container-runtime.service
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ OnFailure=container-runtime-failure.service
[Service]
Type=simple
Environment=RUST_LOG=startos=debug
ExecStart=/usr/bin/node --experimental-detect-module --trace-warnings --unhandled-rejections=warn /usr/lib/startos/init/index.js
ExecStart=/usr/bin/node --experimental-detect-module --trace-warnings /usr/lib/startos/init/index.js
Restart=no

[Install]
Expand Down
103 changes: 93 additions & 10 deletions container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,74 @@ function todo(): never {
throw new Error("Not implemented")
}

function getStatus(
effects: Effects,
options: Omit<Parameters<Effects["getStatus"]>[0], "callback"> = {},
) {
async function* watch(abort?: AbortSignal) {
const resolveCell = { resolve: () => {} }
effects.onLeaveContext(() => {
resolveCell.resolve()
})
abort?.addEventListener("abort", () => resolveCell.resolve())
while (effects.isInContext && !abort?.aborted) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
yield await effects.getStatus({ ...options, callback })
await waitForNext
}
}
return {
const: () =>
effects.getStatus({
...options,
callback:
effects.constRetry &&
(() => effects.constRetry && effects.constRetry()),
}),
once: () => effects.getStatus(options),
watch: (abort?: AbortSignal) => {
const ctrl = new AbortController()
abort?.addEventListener("abort", () => ctrl.abort())
return watch(ctrl.signal)
},
onChange: (
callback: (
value: T.StatusInfo | null,
error?: Error,
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
) => {
;(async () => {
const ctrl = new AbortController()
for await (const value of watch(ctrl.signal)) {
try {
const res = await callback(value)
if (res.cancel) {
ctrl.abort()
break
}
} catch (e) {
console.error(
"callback function threw an error @ getStatus.onChange",
e,
)
}
}
})()
.catch((e) => callback(null, e as Error))
.catch((e) =>
console.error(
"callback function threw an error @ getStatus.onChange",
e,
),
)
},
}
}

/**
* Local type for procedure values from the manifest.
* The manifest's zod schemas use ZodTypeAny casts that produce `unknown` in zod v4.
Expand Down Expand Up @@ -1046,16 +1114,26 @@ export class SystemForEmbassy implements System {
timeoutMs: number | null,
): Promise<void> {
// TODO: docker
await effects.mount({
location: `/media/embassy/${id}`,
target: {
packageId: id,
volumeId: "embassy",
subpath: null,
readonly: true,
idmap: [],
},
})
const status = await getStatus(effects, { packageId: id }).const()
if (!status) return
try {
await effects.mount({
location: `/media/embassy/${id}`,
target: {
packageId: id,
volumeId: "embassy",
subpath: null,
readonly: true,
idmap: [],
},
})
} catch (e) {
console.error(
`Failed to mount dependency volume for ${id}, skipping autoconfig:`,
e,
)
return
}
configFile
.withPath(`/media/embassy/${id}/config.json`)
.read()
Expand Down Expand Up @@ -1204,6 +1282,11 @@ async function updateConfig(
if (specValue.target === "config") {
const jp = require("jsonpath")
const depId = specValue["package-id"]
const depStatus = await getStatus(effects, { packageId: depId }).const()
if (!depStatus) {
mutConfigValue[key] = null
continue
}
await effects.mount({
location: `/media/embassy/${depId}`,
target: {
Expand Down
17 changes: 9 additions & 8 deletions core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,7 @@ once_cell = "1.19.0"
openssh-keys = "0.6.2"
openssl = { version = "0.10.57", features = ["vendored"] }
p256 = { version = "0.13.2", features = ["pem"] }
patch-db = { version = "*", path = "../patch-db/patch-db", features = [
"trace",
] }
patch-db = { version = "*", path = "../patch-db/core", features = ["trace"] }
pbkdf2 = "0.12.2"
pin-project = "1.1.3"
pkcs8 = { version = "0.10.2", features = ["std"] }
Expand Down
26 changes: 20 additions & 6 deletions core/locales/i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,13 @@ error.set-sys-info:
fr_FR: "Erreur de Définition des Infos Système"
pl_PL: "Błąd Ustawiania Informacji o Systemie"

error.bios:
en_US: "BIOS/UEFI Error"
de_DE: "BIOS/UEFI-Fehler"
es_ES: "Error de BIOS/UEFI"
fr_FR: "Erreur BIOS/UEFI"
pl_PL: "Błąd BIOS/UEFI"

# disk/main.rs
disk.main.disk-not-found:
en_US: "StartOS disk not found."
Expand Down Expand Up @@ -2914,6 +2921,13 @@ help.arg.log-limit:
fr_FR: "Nombre maximum d'entrées de journal"
pl_PL: "Maksymalna liczba wpisów logu"

help.arg.merge:
en_US: "Merge with existing version range instead of replacing"
de_DE: "Mit vorhandenem Versionsbereich zusammenführen statt ersetzen"
es_ES: "Combinar con el rango de versiones existente en lugar de reemplazar"
fr_FR: "Fusionner avec la plage de versions existante au lieu de remplacer"
pl_PL: "Połącz z istniejącym zakresem wersji zamiast zastępować"

help.arg.mirror-url:
en_US: "URL of the mirror"
de_DE: "URL des Spiegels"
Expand Down Expand Up @@ -5204,12 +5218,12 @@ about.reset-user-interface-password:
fr_FR: "Réinitialiser le mot de passe de l'interface utilisateur"
pl_PL: "Zresetuj hasło interfejsu użytkownika"

about.reset-webserver:
en_US: "Reset the webserver"
de_DE: "Den Webserver zurücksetzen"
es_ES: "Restablecer el servidor web"
fr_FR: "Réinitialiser le serveur web"
pl_PL: "Zresetuj serwer internetowy"
about.uninitialize-webserver:
en_US: "Uninitialize the webserver"
de_DE: "Den Webserver deinitialisieren"
es_ES: "Desinicializar el servidor web"
fr_FR: "Désinitialiser le serveur web"
pl_PL: "Zdezinicjalizuj serwer internetowy"

about.restart-server:
en_US: "Restart the server"
Expand Down
4 changes: 1 addition & 3 deletions core/src/backup/backup_bulk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,7 @@ async fn perform_backup(
os_backup_file.save().await?;

let luks_folder_old = backup_guard.path().join("luks.old");
if tokio::fs::metadata(&luks_folder_old).await.is_ok() {
tokio::fs::remove_dir_all(&luks_folder_old).await?;
}
crate::util::io::delete_dir(&luks_folder_old).await?;
let luks_folder_bak = backup_guard.path().join("luks");
if tokio::fs::metadata(&luks_folder_bak).await.is_ok() {
tokio::fs::rename(&luks_folder_bak, &luks_folder_old).await?;
Expand Down
8 changes: 4 additions & 4 deletions core/src/bins/container_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ use crate::service::cli::{ContainerCliContext, ContainerClientConfig};
use crate::util::logger::LOGGER;
use crate::version::{Current, VersionT};

lazy_static::lazy_static! {
static ref VERSION_STRING: String = Current::default().semver().to_string();
}

pub fn main(args: impl IntoIterator<Item = OsString>) {
LOGGER.enable();
if let Err(e) = CliApp::new(
|cfg: ContainerClientConfig| Ok(ContainerCliContext::init(cfg)),
crate::service::effects::handler(),
)
.mutate_command(super::translate_cli)
.mutate_command(|cmd| {
cmd.name("start-container")
.version(Current::default().semver().to_string())
})
.run(args)
{
match e.data {
Expand Down
Loading