aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat
-rw-r--r--.dockerignore15+0 −15
-rw-r--r--.editorconfig2+1 −1
-rw-r--r--.github/workflows/build.yml156+18 −138
-rw-r--r--.github/workflows/legal.yml35+0 −35
-rw-r--r--.gitignore10+1 −9
-rw-r--r--CLA.md111+69 −42
-rw-r--r--CODEOWNERS2+1 −1
-rw-r--r--LICENSE.go2+1 −1
-rw-r--r--LICENSES/CC-BY-3.0.txt319+0 −319
-rw-r--r--README.md80+40 −40
-rw-r--r--REUSE.toml18+4 −14
-rw-r--r--Taskfile.yml250+163 −87
-rw-r--r--VERSION.go2+1 −1
-rw-r--r--buf.yaml2+1 −1
-rw-r--r--charts/mirum/Chart.yaml10+0 −10
-rw-r--r--charts/mirum/templates/_helpers.tpl40+0 −40
-rw-r--r--charts/mirum/templates/server-deployment.yaml142+0 −142
-rw-r--r--charts/mirum/templates/server-httproute.yaml26+0 −26
-rw-r--r--charts/mirum/templates/server-service.yaml23+0 −23
-rw-r--r--charts/mirum/templates/server-tcproute.yaml22+0 −22
-rw-r--r--charts/mirum/templates/worker-deployment.yaml87+0 −87
-rw-r--r--charts/mirum/values.yaml75+0 −75
-rw-r--r--cmd/mirum-agent/build.zig113+0 −113
-rw-r--r--cmd/mirum-agent/build.zig.zon17+0 −17
-rw-r--r--cmd/mirum-agent/main.c10+0 −10
-rw-r--r--cmd/mirum-agent/mirum-agent.c10+0 −10
-rw-r--r--cmd/mirum-agent/mirum-agent.h9+0 −9
-rw-r--r--cmd/mirum-agent/test.c22+0 −22
-rw-r--r--cmd/mirum-server/Dockerfile20+0 −20
-rw-r--r--cmd/mirum-server/actor.go2+1 −1
-rw-r--r--cmd/mirum-server/api_cli.go41+1 −40
-rw-r--r--cmd/mirum-server/cert.go2+1 −1
-rw-r--r--cmd/mirum-server/cert_test.go2+1 −1
-rw-r--r--cmd/mirum-server/config.go2+1 −1
-rw-r--r--cmd/mirum-server/database.go130+36 −94
-rw-r--r--cmd/mirum-server/id.go2+1 −1
-rw-r--r--cmd/mirum-server/id_test.go2+1 −1
-rw-r--r--cmd/mirum-server/licenses.go2+1 −1
-rw-r--r--cmd/mirum-server/main.go2+1 −1
-rw-r--r--cmd/mirum-server/proto/api.proto21+1 −20
-rw-r--r--cmd/mirum-server/proto/buf.gen.yaml2+1 −1
-rw-r--r--cmd/mirum-server/server.go2+1 −1
-rw-r--r--cmd/mirum-server/server_admin.go27+3 −24
-rw-r--r--cmd/mirum-server/server_grpc.go2+1 −1
-rw-r--r--cmd/mirum-server/server_web.go2+1 −1
-rw-r--r--cmd/mirum-server/static.go2+1 −1
-rw-r--r--cmd/mirum-server/static_dev.go2+1 −1
-rw-r--r--cmd/mirum-server/static_prod.go2+1 −1
-rw-r--r--cmd/mirum-server/web/.prettierignore2+1 −1
-rw-r--r--cmd/mirum-server/web/api/client.ts2+1 −1
-rw-r--r--cmd/mirum-server/web/components/pages/dashboard.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/pages/error.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/pages/licenses.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/pages/login.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/accordion.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/button.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/card.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/field.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/input.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/label.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/components/ui/separator.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/entries/dashboard.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/entries/error.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/entries/licenses.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/entries/login.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/eslint.config.js2+1 −1
-rw-r--r--cmd/mirum-server/web/index.css2+1 −1
-rw-r--r--cmd/mirum-server/web/lib/errors.ts6+1 −5
-rw-r--r--cmd/mirum-server/web/lib/mount.tsx2+1 −1
-rw-r--r--cmd/mirum-server/web/lib/utils.ts2+1 −1
-rw-r--r--cmd/mirum-server/web/shadcn.css2+1 −1
-rw-r--r--cmd/mirum-server/web/shell.html2+1 −1
-rw-r--r--cmd/mirum-server/web/tsconfig.json2+1 −1
-rw-r--r--cmd/mirum-server/web/vite.config.ts2+1 −1
-rw-r--r--cmd/mirum-worker/Dockerfile26+0 −26
-rw-r--r--cmd/mirum-worker/client.go6+2 −4
-rw-r--r--cmd/mirum-worker/config.go2+1 −1
-rw-r--r--cmd/mirum-worker/main.go2+1 −1
-rw-r--r--cmd/mirum/main.go2+1 −1
-rw-r--r--go.mod16+8 −8
-rw-r--r--go.sum15+0 −15
-rw-r--r--internal/config/config.go2+1 −1
-rw-r--r--internal/executor/environment.go63+0 −63
-rw-r--r--internal/executor/executor.go103+75 −28
-rw-r--r--internal/executor/host/host.go190+0 −190
-rw-r--r--internal/executor/host/maxrss_other.go11+0 −11
-rw-r--r--internal/executor/host/maxrss_unix.go27+0 −27
-rw-r--r--internal/executor/runtime.go125+0 −125
-rw-r--r--internal/forges/forge.go2+1 −1
-rw-r--r--internal/forges/github.go2+1 −1
-rw-r--r--internal/protocol/backoff.go2+1 −1
-rw-r--r--internal/protocol/backoff_test.go2+1 −1
-rw-r--r--internal/protocol/handshake.go2+1 −1
-rw-r--r--internal/protocol/handshake_test.go2+1 −1
-rw-r--r--internal/protocol/platform.go2+1 −1
-rw-r--r--internal/protocol/platform_test.go2+1 −1
-rw-r--r--internal/protocol/proto/buf.gen.yaml2+1 −1
-rw-r--r--internal/protocol/proto/wire.proto2+1 −1
-rw-r--r--internal/supervisor/supervisor.go2+1 −1
-rw-r--r--internal/supervisor/systemd.go2+1 −1
-rw-r--r--mise.lock84+0 −84
-rw-r--r--mise.toml38+0 −38
-rw-r--r--nfpm.yaml15+4 −11
-rw-r--r--packaging/apt-ftparchive.conf29+29 −0
-rw-r--r--packaging/dl/404.html2+1 −1
-rw-r--r--packaging/dl/index.html2+1 −1
-rw-r--r--packaging/mirum-server.initd2+1 −1
-rw-r--r--packaging/mirum-server.service2+1 −1
-rw-r--r--packaging/mirum-worker.initd2+1 −1
-rw-r--r--packaging/mirum-worker@.service2+1 −1
-rw-r--r--packaging/scripts/postinstall.sh2+1 −1
-rw-r--r--packaging/scripts/preinstall.sh2+1 −1
-rw-r--r--packaging/scripts/preremove.sh2+1 −1
-rw-r--r--packaging/server/config.yaml2+1 −1
-rw-r--r--packaging/worker/default.yaml2+1 −1
-rw-r--r--tools/licensegen/main.go2+1 −1
-rw-r--r--tools/licensegen/scan_go.go2+1 −1
-rw-r--r--tools/licensegen/scan_npm.go2+1 −1
118 files changed, 530 insertions, 2213 deletions
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
--- a/.dockerignore
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-.git
-.github
-.task
-build
-cmd/mirum-agent/.zig-cache
-cmd/mirum-agent/zig-out
-cmd/mirum-agent/zig-pkg
-cmd/mirum-server/apipb
-cmd/mirum-server/static
-cmd/mirum-server/web/gen
-cmd/mirum-server/web/node_modules
-internal/protocol/wirepb
diff --git a/.editorconfig b/.editorconfig
index 7ddec49..61d0094 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
root = true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7e1bd0e..a8310bb 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
# NOTE: Don't extend it; keep the logic in the Taskfile.
@@ -18,11 +18,6 @@ jobs:
build:
name: Build and publish Nightly
runs-on: ubuntu-latest
- concurrency:
- group: >-
- package-publish-mirum-${{ github.ref == 'refs/heads/main' && 'nightly'
- || startsWith(github.ref, 'refs/tags/v') && 'stable' || github.ref }}
- cancel-in-progress: false
permissions:
contents: write
steps:
@@ -30,14 +25,17 @@ jobs:
with:
fetch-depth: 0
- - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3
+ - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
- version: 2026.7.5
- experimental: true
- install: false
+ go-version-file: go.mod
- - name: Install dependencies
- run: mise bootstrap --locked --yes --update
+ - name: Install tools
+ run: |
+ echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list
+ sudo apt update && sudo apt install nfpm apt-utils rclone createrepo-c
+ sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
+ curl -sSL "https://github.com/bufbuild/buf/releases/download/v1.67.0/buf-$(uname -s)-$(uname -m)" -o /usr/local/bin/buf && chmod +x /usr/local/bin/buf
+ pipx install reuse
- name: Build and package
run: task package GIT_REF=${{ github.ref }} GPG_KEY_ID=${{ vars.GPG_KEY_ID }}
@@ -45,7 +43,6 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
APK_PRIVATE_KEY: ${{ secrets.APK_PRIVATE_KEY }}
- PACKAGE_KEY_VERSION: ${{ vars.PACKAGE_KEY_VERSION }}
- name: Lint
run: task lint
@@ -53,17 +50,6 @@ jobs:
- name: Tests
run: task test
- - name: Upload container binaries
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- with:
- name: container-binaries
- path: |
- build/mirum-server-linux-amd64
- build/mirum-server-linux-arm64
- build/mirum-worker-linux-amd64
- build/mirum-worker-linux-arm64
- if-no-files-found: error
-
- name: Publish
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
run: |
@@ -71,123 +57,17 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
. build/dist/build.env
task publish:release CHANNEL=$CHANNEL VERSION=$VERSION
- mise run publish -- \
- --service mirum \
- --channel "$CHANNEL" \
- --input build/dist \
- deb rpm apk
+ task publish:repos \
+ CHANNEL=$CHANNEL \
+ S3_BUCKET=${{ vars.S3_BUCKET }} \
+ S3_PUBLIC_URL=${{ vars.S3_PUBLIC_URL }} \
+ S3_PROVIDER="${{ vars.S3_PROVIDER || 'Other' }}" \
+ S3_REGION=${{ vars.S3_REGION }} \
+ S3_ENDPOINT=${{ vars.S3_ENDPOINT }} \
+ GPG_KEY_ID=${{ vars.GPG_KEY_ID }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
- GPG_KEY_ID: ${{ vars.GPG_KEY_ID }}
- APK_PRIVATE_KEY: ${{ secrets.APK_PRIVATE_KEY }}
- PACKAGE_KEY_VERSION: ${{ vars.PACKAGE_KEY_VERSION }}
- S3_BUCKET: ${{ vars.S3_BUCKET }}
- S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
- S3_PUBLIC_URL: ${{ vars.S3_PUBLIC_URL }}
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
-
- oci:
- name: OCI artifacts
- needs: [build]
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
- steps:
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
-
- - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3
- with:
- version: 2026.7.5
- experimental: true
- install: false
-
- - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- with:
- name: container-binaries
- path: .container
-
- - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
-
- - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
-
- - name: Determine artifact version
- id: artifact
- shell: bash
- run: |
- base_version=$(cat VERSION)
- if [[ "$GITHUB_REF" == refs/heads/main ]]; then
- version="$base_version-nightly.$(git log -1 --format=%ct)"
- elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then
- version="${GITHUB_REF_NAME#v}"
- if [[ "$version" != "$base_version" ]]; then
- echo "Tag version $version does not match VERSION $base_version" >&2
- exit 1
- fi
- else
- version="$base_version-pr.$GITHUB_RUN_NUMBER"
- fi
- echo "version=$version" >> "$GITHUB_OUTPUT"
-
- - name: Log in to GHCR
- if: github.event_name != 'pull_request'
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Log Helm in to GHCR
- if: github.event_name != 'pull_request'
- env:
- GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- printf '%s' "$GHCR_TOKEN" | mise x helm@4.1.1 -- \
- helm registry login ghcr.io --username "$GITHUB_ACTOR" --password-stdin
-
- - name: Build and publish OCI artifacts
- shell: bash
- run: |
- version="${{ steps.artifact.outputs.version }}"
- image_output=()
- chart_output=()
-
- if [[ "${{ github.event_name }}" != pull_request ]]; then
- image_output+=(--push)
- chart_output+=(--push "oci://ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/charts")
- fi
-
- for service in server worker; do
- image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/mirum-$service"
- tags=(
- --tag "$image:sha-$GITHUB_SHA"
- --tag "$image:$version"
- )
- labels=(
- --label "org.opencontainers.image.revision=$GITHUB_SHA"
- --label "org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
- --label "org.opencontainers.image.version=$version"
- )
-
- if [[ "$GITHUB_REF" == refs/heads/main ]]; then
- tags+=(--tag "$image:nightly")
- elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then
- tags+=(--tag "$image:latest")
- fi
-
- mise run container -- \
- --context . \
- --file "cmd/mirum-$service/Dockerfile" \
- --platform linux/amd64,linux/arm64 \
- --cache-scope "mirum-$service" \
- "${tags[@]}" "${labels[@]}" "${image_output[@]}"
- done
-
- mise run chart -- \
- --chart charts/mirum \
- --version "$version" \
- --app-version "$version" \
- "${chart_output[@]}"
diff --git a/.github/workflows/legal.yml b/.github/workflows/legal.yml
deleted file mode 100644
--- a/.github/workflows/legal.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-name: Legal
-
-on:
- push:
- branches: [main]
- tags: ["v*"]
- pull_request:
- branches: [main]
-
-permissions:
- contents: read
-
-jobs:
- legal:
- name: Legal checks
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- with:
- fetch-depth: 0
-
- - uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3
- with:
- version: 2026.7.5
- experimental: true
- install: false
-
- - name: Check contribution sign-off
- run: mise run signoff
-
- - name: Check licensing policy
- run: mise run licenses
diff --git a/.gitignore b/.gitignore
index 437dc77..e9e681e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,12 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
/dev
/build
-/.container
/.task
-mise.local.toml
-mise.*.local.toml
cmd/mirum-server/static
cmd/mirum-server/web/node_modules
-# zig build artifacts
-.zig-cache/
-zig-out/
-zig-pkg/
-
# grpc generated code
/internal/protocol/wirepb
/cmd/mirum-server/apipb
diff --git a/CLA.md b/CLA.md
index d0140e8..736d407 100644
--- a/CLA.md
+++ b/CLA.md
@@ -1,45 +1,43 @@
-# Mirum Individual Contributor License Agreement
+# Mirum Grant and Contributor License Agreement
-Version 1.0
+> This agreement is based on the Harmony Combined Contributor Agreement
+> Version 1.0 licensed under a [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/).
-> This agreement is based on the Harmony Individual Contributor License
-> Agreement Version 1.0 licensed under a
-> [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/).
-
-Thank you for your interest in contributing to Mirum (the "Project"). In this
-Agreement, "We" and "Us" mean Nikolay Govorov.
-
-This contributor agreement ("Agreement") documents the rights granted by
-contributors to Us. To make this document effective, You must personally add
-the following trailers to every commit You Submit:
-
-```text
-CLA-Version: 1.0
-Signed-off-by: Your Name <your.email@example.com>
-```
-
-The name and email address in `Signed-off-by` must identify You and match the
-commit author. By Submitting a commit containing these trailers, You
-electronically sign and accept this Agreement. No other person or automated
-system may add the `Signed-off-by` trailer on Your behalf. This is a legally
-binding document, so please read it carefully before agreeing to it.
+Thank you for your interest in contributing to Mirum ("We" or "Us"). In order
+to clarify the intellectual property license granted with Contributions from any
+person or entity, We must have a Contributor License Agreement ("CLA") on file
+that has been signed, accepted or otherwise agreed to by each contributor,
+indicating agreement to the license terms below. This license is for your
+protection as a contributor as well as the protection of the Us and our users;
+it does not change your rights to use your own Contributions for any other
+purpose.
## 1. Definitions
-"You" means the individual who Submits a Contribution to Us.
+"You" (Individual) means the individual who Submits a Contribution to Us.
+
+"You" (Entity) means any Legal Entity on behalf of whom a Contribution has been
+received by Us. "Legal Entity" means an entity which is not a natural person.
+"Affiliates" means other Legal Entities that control, are controlled by, or
+under common control with that Legal Entity. For the purposes of this
+definition, "control" means (i) the power, direct or indirect, to cause the
+direction or management of such Legal Entity, whether by contract or otherwise,
+(ii) ownership of fifty percent (50%) or more of the outstanding shares or
+securities which vote to elect the management or other persons who direct such
+Legal Entity or (iii) beneficial ownership of such entity.
"Contribution" means any work of authorship that is Submitted by You to Us in
-which You own or assert ownership of the Copyright. If You do not own the
-Copyright in the entire work of authorship, please follow the instructions in
-Section 3(d).
+which You own or assert ownership of the Copyright.
"Copyright" means all rights protecting works of authorship owned or controlled
-by You, including copyright, moral and neighboring rights, as appropriate, for
-the full term of their existence including any extensions by You.
+by You [or Your Affiliates], including copyright, moral and neighboring rights,
+as appropriate, for the full term of their existence including any extensions by
+You.
"Material" means the work of authorship which is made available by Us to third
-parties as part of the Project. After You Submit the Contribution, it may be
-included in the Material.
+parties. When this Agreement covers more than one software project, the Material
+means the work of authorship to which the Contribution was Submitted. After You
+Submit the Contribution, it may be included in the Material.
"Submit" means any form of electronic, verbal, or written communication sent to
Us or our representatives, including but not limited to electronic mailing
@@ -71,10 +69,40 @@ modify, display, perform and distribute the Contribution as part of the
Material; provided that this license is conditioned upon compliance with Section
2.3.
+### 2.1 Copyright Assignment
+
+(a) At the time the Contribution is Submitted, You assign to Us all right,
+title, and interest worldwide in all Copyright covering the Contribution;
+provided that this transfer is conditioned upon compliance with Section 2.3.
+
+(b) To the extent that any of the rights in Section 2.1(a) cannot be assigned by
+You to Us, You grant to Us a perpetual, worldwide, exclusive, royalty-free,
+transferable, irrevocable license under such non-assigned rights, with rights to
+sublicense through multiple tiers of sublicensees, to practice such non-assigned
+rights, including, but not limited to, the right to reproduce, modify, display,
+perform and distribute the Contribution; provided that this license is
+conditioned upon compliance with Section 2.3.
+
+(c) To the extent that any of the rights in Section 2.1(a) can neither be
+assigned nor licensed by You to Us, You irrevocably waive and agree never to
+assert such rights against Us, any of our successors in interest, or any of our
+licensees, either direct or indirect; provided that this agreement not to assert
+is conditioned upon compliance with Section 2.3.
+
+(d) Upon such transfer of rights to Us, to the maximum extent possible, We
+immediately grant to You a perpetual, worldwide, non-exclusive, royalty-free,
+transferable, irrevocable license under such rights covering the Contribution,
+with rights to sublicense through multiple tiers of sublicensees, to reproduce,
+modify, display, perform, and distribute the Contribution. The intention of the
+parties is that this license will be as broad as possible and to provide You
+with rights as similar as possible to the owner of the rights that You
+transferred. This license back is limited to the Contribution and does not
+provide any rights to the Material.
+
### 2.2 Patent License
For patent claims including, without limitation, method, process, and apparatus
-claims which You own, control or have the right to grant,
+claims which You [or Your Affiliates] own, control or have the right to grant,
now or in the future, You grant to Us a perpetual, worldwide, non-exclusive,
transferable, royalty-free, irrevocable patent license, with the right to
sublicense these rights to multiple tiers of sublicensees, to make, have made,
@@ -106,7 +134,7 @@ the Material and may decide to include any Contribution We consider appropriate.
### 2.6 Reservation of Rights
-Any rights not expressly licensed under this section are expressly
+Any rights not expressly [assigned or] licensed under this section are expressly
reserved by You.
## 3. Agreement
@@ -115,32 +143,31 @@ You confirm that:
(a) You have the legal authority to enter into this Agreement.
-(b) You own the Copyright and patent claims covering the
+(b) You [or Your Affiliates] own the Copyright and patent claims covering the
Contribution which are required to grant the rights under Section 2.
-(c) The grant of rights under Section 2 does not violate any grant
+(c) (Individual) The grant of rights under Section 2 does not violate any grant
of rights which You have made to third parties, including Your employer. If You
are an employee, You have had Your employer approve this Agreement or sign the
Entity version of this document. If You are less than eighteen years old, please
have Your parents or guardian sign the Agreement.
-(d) If You do not own the Copyright in the entire work of authorship Submitted,
-You have clearly identified the third-party work, its source, and its license in
-the Submission.
+(c) (Entity) The grant of rights under Section 2 does not violate any grant of
+rights which You or Your Affiliates have made to third parties.
## 4. Disclaimer
EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS
IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT
LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US.
-TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY
+PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US [AND BY US TO
+YOU]. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY
IS LIMITED IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW.
## 5. Consequential Damage Waiver
-TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE
-LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA,
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU [OR US]
+BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA,
INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT
OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR
OTHERWISE) UPON WHICH THE CLAIM IS BASED.
diff --git a/CODEOWNERS b/CODEOWNERS
index 7ca9ac5..f496bf2 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
* @mrdimidium
diff --git a/LICENSE.go b/LICENSE.go
index d782ffe..c8e2d72 100644
--- a/LICENSE.go
+++ b/LICENSE.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
//go:build !licensegen
diff --git a/LICENSES/CC-BY-3.0.txt b/LICENSES/CC-BY-3.0.txt
deleted file mode 100644
--- a/LICENSES/CC-BY-3.0.txt
+++ /dev/null
@@ -1,319 +0,0 @@
-Creative Commons Legal Code
-
-Attribution 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work or Adaptation, as appropriate, through sale or
- other transfer of ownership.
- d. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- e. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- f. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- h. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- i. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
- b. to create and Reproduce Adaptations provided that any such Adaptation,
- including any translation in any medium, takes reasonable steps to
- clearly label, demarcate or otherwise identify that changes were made
- to the original Work. For example, a translation could be marked "The
- original work was translated from English to Spanish," or a
- modification could indicate "The original work has been modified.";
- c. to Distribute and Publicly Perform the Work including as incorporated
- in Collections; and,
- d. to Distribute and Publicly Perform Adaptations.
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor waives the
- exclusive right to collect such royalties for any exercise by You
- of the rights granted under this License; and,
- iii. Voluntary License Schemes. The Licensor waives the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats. Subject to Section 8(f), all rights not expressly
-granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(b), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the extent
- practicable, remove from the Adaptation any credit as required by
- Section 4(b), as requested.
- b. If You Distribute, or Publicly Perform the Work or any Adaptations or
- Collections, You must, unless a request has been made pursuant to
- Section 4(a), keep intact all copyright notices for the Work and
- provide, reasonable to the medium or means You are utilizing: (i) the
- name of the Original Author (or pseudonym, if applicable) if supplied,
- and/or if the Original Author and/or Licensor designate another party
- or parties (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work;
- and (iv) , consistent with Section 3(b), in the case of an Adaptation,
- a credit identifying the use of the Work in the Adaptation (e.g.,
- "French translation of the Work by Original Author," or "Screenplay
- based on original Work by Original Author"). The credit required by
- this Section 4 (b) may be implemented in any reasonable manner;
- provided, however, that in the case of a Adaptation or Collection, at
- a minimum such credit will appear, if a credit for all contributing
- authors of the Adaptation or Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- c. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Adaptations or Collections, You must not distort, mutilate, modify or
- take other derogatory action in relation to the Work which would be
- prejudicial to the Original Author's honor or reputation. Licensor
- agrees that in those jurisdictions (e.g. Japan), in which any exercise
- of the right granted in Section 3(b) of this License (the right to
- make Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the Original
- Author's honor and reputation, the Licensor will waive or not assert,
- as appropriate, this Section, to the fullest extent permitted by the
- applicable national law, to enable You to reasonably exercise Your
- right under Section 3(b) of this License (right to make Adaptations)
- but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Adaptations or Collections
- from You under this License, however, will not have their licenses
- terminated provided such individuals or entities remain in full
- compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
- survive any termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
- offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- d. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- f. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of this License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/README.md b/README.md
index a9f4640..d5e15aa 100644
--- a/README.md
+++ b/README.md
@@ -9,18 +9,6 @@
An experimental portable CI platform with VM-first isolation,
programmable pipelines, local execution parity and Starlark configs.
-## Development
-
-Install the language toolchains, build utilities, and required system packages
-declared by the repository:
-
-```console
-mise bootstrap
-mise exec -- task --list
-```
-
-`task` remains the runner until the task definitions are migrated to mise.
-
## [Why](https://xkcd.com/927/)
The goal of this project is to build a CI system that's both convenient for tiny
@@ -139,8 +127,8 @@ Please note that the project is in its infancy and is **not** intended for produ
```bash
sudo apt install curl gnupg
-curl -fsSL https://pkg.dimidiumlabs.io/packages.gpg | sudo gpg --dearmor -o /usr/share/keyrings/dimidiumlabs.gpg
-echo "deb [signed-by=/usr/share/keyrings/dimidiumlabs.gpg] https://pkg.dimidiumlabs.io/mirum/apt/ nightly main" | sudo tee /etc/apt/sources.list.d/mirum.list
+curl -fsSL https://dl.mirum.dev/public.gpg | sudo gpg --dearmor -o /usr/share/keyrings/mirum.gpg
+echo "deb [signed-by=/usr/share/keyrings/mirum.gpg] https://dl.mirum.dev/apt/ nightly main" | sudo tee /etc/apt/sources.list.d/mirum.list
sudo apt update && sudo apt install mirum
# Start the server
@@ -154,10 +142,10 @@ sudo systemctl enable --now mirum-worker@default
```bash
# DNF5 (Fedora 41+, RHEL 10+)
-sudo dnf config-manager addrepo --from-repofile=https://pkg.dimidiumlabs.io/mirum/rpm/nightly/mirum-nightly.repo
+sudo dnf config-manager addrepo --from-repofile=https://dl.mirum.dev/rpm/nightly/mirum-nightly.repo
# DNF4 (Fedora 40 and older, RHEL 8/9)
-sudo curl -o /etc/yum.repos.d/mirum-nightly.repo https://pkg.dimidiumlabs.io/mirum/rpm/nightly/mirum-nightly.repo
+sudo curl -o /etc/yum.repos.d/mirum-nightly.repo https://dl.mirum.dev/rpm/nightly/mirum-nightly.repo
sudo dnf install mirum
@@ -171,8 +159,8 @@ sudo systemctl enable --now mirum-worker@default
**openSUSE:**
```bash
-sudo rpm --import https://pkg.dimidiumlabs.io/packages.gpg
-sudo zypper addrepo https://pkg.dimidiumlabs.io/mirum/rpm/nightly/ mirum-nightly
+sudo rpm --import https://dl.mirum.dev/public.gpg
+sudo zypper addrepo https://dl.mirum.dev/rpm/nightly/ mirum-nightly
sudo zypper refresh
sudo zypper install mirum
@@ -185,11 +173,15 @@ sudo systemctl enable --now mirum-worker@default
**Alpine:**
+No repository yet — grab the signing key and the signed `.apk` from the nightly
+release:
+
```sh
-sudo wget -O /etc/apk/keys/packages.0001.rsa.pub https://pkg.dimidiumlabs.io/keys/packages.0001.rsa.pub
-echo "https://pkg.dimidiumlabs.io/mirum/apk/nightly" | sudo tee -a /etc/apk/repositories
-sudo apk update
-sudo apk add mirum
+sudo wget -O /etc/apk/keys/mirum.rsa.pub https://dl.mirum.dev/mirum.rsa.pub
+
+# Download the .apk for your arch from
+# https://github.com/dimidiumlabs/mirum/releases/tag/nightly
+sudo apk add ./mirum_<version>_<arch>.apk
# Start the server
sudo rc-update add mirum-server default
@@ -204,29 +196,28 @@ sudo rc-service mirum-worker.default start
## Contributing
-We welcome your contributions, including code, bug reports, ideas, and success
-stories.
+We welcome your contributions, including code, bug reports, ideas, and success stories.
-If you are making a contribution for the first time or from a new email, please
-add yourself to the `.mailmap`.
+If you are making a contribution for the first time or from a new email,
+please add yourself to the `.mailmap`.
-### Signoff
+### Contributor License Agreement
-To include your code, we ask that you read and agree to the [CLA](./CLA.md). To
-sign, add a `CLA-Version: 1.0` and a `Signed-off-by` trailer to every commit
-(`git commit -s --trailer "CLA-Version: 1.0"`). Each commit in a pull request
-must carry a valid `Signed-off-by` line matching the commit author. Please use
-your real name. We cannot include code from anonymous contributors.
+To include your code, we ask that you read and agree to the [CLA](./CLA.md).
+To sign, add a `Signed-off-by` trailer to every commit (`git commit -s`).
+Each commit in a pull request must carry a valid `Signed-off-by`
+line matching the commit author. Please use your real name or your
+public nickname. We cannot include code from anonymous contributors.
-AI agents MUST NOT add Signed-off-by tags. Only humans can legally certify the
-Contributor License Agreement.
+AI agents MUST NOT add Signed-off-by tags.
+Only humans can legally certify the Contributor License Agreement.
### AI policy
You may use AI agents when writing code and documentation. AI is not allowed for
media including images, videos, fonts at all. You must fully read, understand,
-and cleanup any code generated by the agent. We ask that you disclose the
-agent's use and indicate the tool, model, and extent of contribution.
+and cleanup any code generated by the agent. We ask that you disclose the agent's
+use and indicate the tool, model, and extent of contribution.
Contributions should include an Assisted-by tag in the following format:
`Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]`, for example:
@@ -234,9 +225,18 @@ Contributions should include an Assisted-by tag in the following format:
Remember, AI agents should make software better, not worse.
-## Licensing
+## License
+
+Copyright (C) 2026 Nikolay Govorov
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Affero General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
-Mirum source code is licensed under AGPL-3.0-or-later. Documentation is licensed
-under CC-BY-4.0.
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-The bundled shadcn UI components are licensed under MIT.
+You should have received a copy of the GNU Affero General Public License along
+with this program. If not, see <https://www.gnu.org/licenses/>.
diff --git a/REUSE.toml b/REUSE.toml
index 708bc13..791cdce 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
version = 1
@@ -6,32 +6,22 @@ version = 1
[[annotations]]
path = [
".mailmap",
+ "CLA.md",
"VERSION",
- "cmd/mirum-agent/build.zig.zon",
"cmd/mirum-server/web/*.json",
"go.mod",
"go.sum",
- "mise.lock",
"packaging/dl/*",
"packaging/logo.svg",
- "charts/mirum/templates/*",
"buf.*",
]
-SPDX-FileCopyrightText = "2026 Nikolay Govorov"
+SPDX-FileCopyrightText = "2026 Nikolay Govorov <me@govorov.online>"
SPDX-License-Identifier = "AGPL-3.0-or-later"
[[annotations]]
path = [
- "CLA.md",
-]
-SPDX-FileCopyrightText = "2026 Nikolay Govorov"
-SPDX-License-Identifier = "CC-BY-3.0"
-
-[[annotations]]
-path = [
"README.md",
- "charts/mirum/README.md",
"docs/**.md",
]
-SPDX-FileCopyrightText = "2026 Nikolay Govorov"
+SPDX-FileCopyrightText = "2026 Nikolay Govorov <me@govorov.online>"
SPDX-License-Identifier = "CC-BY-4.0"
diff --git a/Taskfile.yml b/Taskfile.yml
index 8e9b060..4ba97ed 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
version: "3"
@@ -16,15 +16,6 @@ vars:
DIST_DIR: build/dist
BUILD_DIR: build
LINUX_PKG_TARGETS: amd64 arm64 riscv64 ppc64le loong64 s390x
- # Guest matrix for mirum-agent — a strict superset of the host cross
- # matrix. Extend here as more guest targets are supported.
- AGENT_TARGETS: >-
- linux-amd64 linux-arm64 linux-riscv64 linux-ppc64le linux-loong64 linux-s390x
- darwin-amd64 darwin-arm64
- windows-amd64 windows-arm64
- freebsd-amd64 freebsd-arm64 freebsd-riscv64
- netbsd-amd64 netbsd-arm64
- openbsd-amd64 openbsd-arm64
tasks:
# Production build pipeline
@@ -123,55 +114,8 @@ tasks:
- go build -ldflags="{{.LDFLAGS}}" -o {{.BUILD_DIR}}/mirum-worker-{{.GOOS}}-{{.GOARCH}} ./cmd/mirum-worker
- go build -ldflags="{{.LDFLAGS}}" -o {{.BUILD_DIR}}/mirum-{{.GOOS}}-{{.GOARCH}} ./cmd/mirum
- agent:build:
- desc: "Build mirum-agent for one guest target (override GOOS/GOARCH)"
- dir: cmd/mirum-agent
- vars:
- GOOS: { sh: "echo ${GOOS:-$(go env GOOS)}" }
- GOARCH: { sh: "echo ${GOARCH:-$(go env GOARCH)}" }
- cmds:
- - |
- case "{{.GOARCH}}" in
- amd64) za=x86_64 ;;
- arm64) za=aarch64 ;;
- riscv64) za=riscv64 ;;
- ppc64le) za=powerpc64le ;;
- loong64) za=loongarch64 ;;
- s390x) za=s390x ;;
- *) echo "agent: unsupported arch {{.GOARCH}}" >&2; exit 1 ;;
- esac
- case "{{.GOOS}}" in
- linux) ztriple="${za}-linux" ;;
- darwin) ztriple="${za}-macos" ;;
- windows) ztriple="${za}-windows-gnu" ;;
- freebsd) ztriple="${za}-freebsd" ;;
- openbsd) ztriple="${za}-openbsd" ;;
- netbsd) ztriple="${za}-netbsd" ;;
- *) echo "agent: unsupported os {{.GOOS}}" >&2; exit 1 ;;
- esac
- ext=""; [ "{{.GOOS}}" = windows ] && ext=.exe
- zig build -Dtarget="$ztriple" -Doptimize=ReleaseFast -Dstrip
- mkdir -p ../../{{.BUILD_DIR}}/agent
- cp "zig-out/bin/mirum-agent${ext}" "../../{{.BUILD_DIR}}/agent/mirum-agent-{{.GOOS}}-{{.GOARCH}}${ext}"
-
- agent:cross:
- desc: Cross-compile mirum-agent for the full guest matrix
- cmds:
- - rm -rf {{.BUILD_DIR}}/agent
- - for: { var: AGENT_TARGETS }
- cmd: |
- target="{{.ITEM}}"
- task agent:build GOOS="${target%-*}" GOARCH="${target##*-}"
-
- agent:test:
- desc: Build and run mirum-agent unit tests
- dir: cmd/mirum-agent
- cmds:
- - zig build test
-
cross:
desc: Cross-compile and archive binaries for all supported platforms
- deps: [agent:cross]
cmds:
- mkdir -p {{.DIST_DIR}}
- for: [
@@ -196,18 +140,14 @@ tasks:
cp -r LICENSES "$staging/"
for cmd in cmd/*/; do
bin=$(basename "$cmd")
- [ "$bin" = "mirum-agent" ] && continue
cp "{{.BUILD_DIR}}/${bin}-${os}-${arch}" "${staging}/${bin}${ext}"
done
- cp -r "{{.BUILD_DIR}}/agent" "$staging/agent"
-
- format=tar.gz
- [ "$os" = "windows" ] && format=zip
- mise run package -- \
- --output "{{.DIST_DIR}}" \
- --archive-root "$staging" \
- --archive-name "mirum-${target}" \
- "$format"
+
+ if [ "$os" = "windows" ]; then
+ (cd "$staging" && zip -qr "$OLDPWD/{{.DIST_DIR}}/mirum-${target}.zip" .)
+ else
+ tar -czf "{{.DIST_DIR}}/mirum-${target}.tar.gz" -C "$staging" .
+ fi
rm -rf "$staging"
- cd {{.DIST_DIR}} && sha256sum *.tar.gz *.zip > SHA256SUMS
@@ -233,29 +173,135 @@ tasks:
VERSION: "{{.VERSION}}"
cmds:
- |
+ cleanup() { rm -rf "$GNUPGHOME" /tmp/mirum-signing.asc /tmp/mirum-apk.rsa 2>/dev/null; }
+ trap cleanup EXIT
+
+ if [ -n "${GPG_PRIVATE_KEY:-}" ]; then
+ export GNUPGHOME=$(mktemp -d)
+ chmod 700 "$GNUPGHOME"
+ printf '%s' "$GPG_PRIVATE_KEY" > /tmp/mirum-signing.asc
+ chmod 600 /tmp/mirum-signing.asc
+ gpg --batch --yes --passphrase "$GPG_PASSPHRASE" --import /tmp/mirum-signing.asc
+ export GPG_KEY_ID="{{.GPG_KEY_ID}}"
+ export SIGNING_PRIVATE_KEY="/tmp/mirum-signing.asc"
+ export NFPM_PASSPHRASE="$GPG_PASSPHRASE"
+ fi
+
+ if [ -n "${APK_PRIVATE_KEY:-}" ]; then
+ printf '%s' "$APK_PRIVATE_KEY" > /tmp/mirum-apk.rsa
+ chmod 600 /tmp/mirum-apk.rsa
+ export APK_SIGNING_KEY="/tmp/mirum-apk.rsa"
+ openssl rsa -in /tmp/mirum-apk.rsa -pubout -out "{{.DIST_DIR}}/mirum.rsa.pub"
+ fi
+
for arch in {{.LINUX_PKG_TARGETS}}; do
mkdir -p {{.BUILD_DIR}}/tmp
for cmd in cmd/*/; do
bin=$(basename "$cmd")
- [ "$bin" = "mirum-agent" ] && continue
cp "{{.BUILD_DIR}}/${bin}-linux-${arch}" "{{.BUILD_DIR}}/tmp/${bin}"
done
-
- formats="deb rpm"
+ ARCH=$arch nfpm package --packager deb --target {{.DIST_DIR}}/
+ ARCH=$arch nfpm package --packager rpm --target {{.DIST_DIR}}/
# Alpine does not build for loong64
if [ "$arch" != "loong64" ]; then
- formats="$formats apk"
+ ARCH=$arch nfpm package --packager apk --target {{.DIST_DIR}}/
fi
- GPG_KEY_ID="{{.GPG_KEY_ID}}" mise run package -- \
- --version "{{.VERSION}}" \
- --arch "$arch" \
- --output "{{.DIST_DIR}}" \
- $formats
rm -rf {{.BUILD_DIR}}/tmp
done
printf 'VERSION={{.VERSION}}\nCHANNEL={{.CHANNEL}}\n' > {{.DIST_DIR}}/build.env
+ publish:repos:
+ desc: Publish packages to APT and RPM repositories on S3
+ requires:
+ vars:
+ [
+ CHANNEL,
+ S3_BUCKET,
+ S3_PUBLIC_URL,
+ S3_PROVIDER,
+ S3_REGION,
+ S3_ENDPOINT,
+ GPG_KEY_ID,
+ ]
+ cmds:
+ - |
+ set -e
+ export GNUPGHOME=$(mktemp -d)
+ RCLONE_CONF=$(mktemp)
+ chmod 700 "$GNUPGHOME"
+ cleanup() { rm -rf "$GNUPGHOME" "$RCLONE_CONF"; }
+ trap cleanup EXIT
+
+ cat > "$RCLONE_CONF" << EOF
+ [s3]
+ type = s3
+ provider = {{.S3_PROVIDER}}
+ access_key_id = ${S3_ACCESS_KEY_ID}
+ secret_access_key = ${S3_SECRET_ACCESS_KEY}
+ region = {{.S3_REGION}}
+ endpoint = {{.S3_ENDPOINT}}
+ EOF
+ export RCLONE_CONFIG="$RCLONE_CONF"
+
+ printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --import
+ echo "allow-loopback-pinentry" >> "$GNUPGHOME/gpg-agent.conf"
+ gpgconf --kill gpg-agent
+
+ # Upload static files
+ gpg --export --armor "{{.GPG_KEY_ID}}" > "$GNUPGHOME/public.gpg"
+ rclone copyto "$GNUPGHOME/public.gpg" "s3:{{.S3_BUCKET}}/public.gpg"
+ rclone copy packaging/dl/ "s3:{{.S3_BUCKET}}/"
+
+ if [ -f "{{.DIST_DIR}}/mirum.rsa.pub" ]; then
+ rclone copyto "{{.DIST_DIR}}/mirum.rsa.pub" "s3:{{.S3_BUCKET}}/mirum.rsa.pub"
+ fi
+
+ # APT repository
+ CHANNEL={{.CHANNEL}}
+ mkdir -p /tmp/apt-cache apt-repo
+ rclone copy "s3:{{.S3_BUCKET}}/apt/" apt-repo/
+
+ for ch in stable nightly; do
+ mkdir -p apt-repo/pool/$ch
+ for arch in {{.LINUX_PKG_TARGETS}}; do
+ mkdir -p apt-repo/dists/$ch/main/binary-$arch
+ done
+ done
+ cp {{.DIST_DIR}}/*.deb apt-repo/pool/$CHANNEL/
+
+ cd apt-repo
+ apt-ftparchive generate "$(pwd)/../packaging/apt-ftparchive.conf"
+ apt-ftparchive \
+ -o APT::FTPArchive::Release::Codename=$CHANNEL \
+ -o APT::FTPArchive::Release::Components=main \
+ -o APT::FTPArchive::Release::Architectures="{{.LINUX_PKG_TARGETS}}" \
+ release dists/$CHANNEL/ > dists/$CHANNEL/Release
+ gpg --default-key="{{.GPG_KEY_ID}}" --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" -abs -o dists/$CHANNEL/Release.gpg dists/$CHANNEL/Release
+ gpg --default-key="{{.GPG_KEY_ID}}" --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --clearsign -o dists/$CHANNEL/InRelease dists/$CHANNEL/Release
+ cd ..
+ rclone copy apt-repo/ "s3:{{.S3_BUCKET}}/apt/" --progress
+
+ # RPM repository
+ mkdir -p rpm-repo
+ rclone copy "s3:{{.S3_BUCKET}}/rpm/$CHANNEL/" rpm-repo/
+
+ cp {{.DIST_DIR}}/*.rpm rpm-repo/
+
+ createrepo_c --update rpm-repo/
+ gpg --default-key="{{.GPG_KEY_ID}}" --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --detach-sign --armor rpm-repo/repodata/repomd.xml
+
+ cat > rpm-repo/mirum-${CHANNEL}.repo << REPOEOF
+ [mirum-${CHANNEL}]
+ name=Mirum ${CHANNEL}
+ gpgkey={{.S3_PUBLIC_URL}}/public.gpg
+ baseurl={{.S3_PUBLIC_URL}}/rpm/${CHANNEL}/
+ enabled=1
+ gpgcheck=1
+ REPOEOF
+
+ rclone copy rpm-repo/ "s3:{{.S3_BUCKET}}/rpm/$CHANNEL/" --progress
+
publish:release:
desc: Create or update a GitHub Release
requires:
@@ -275,11 +321,11 @@ tasks:
done
fi
- gh release create "$TAG" {{.DIST_DIR}}/*.deb {{.DIST_DIR}}/*.rpm {{.DIST_DIR}}/*.apk {{.DIST_DIR}}/*.tar.gz {{.DIST_DIR}}/*.zip {{.DIST_DIR}}/SHA256SUMS \
+ gh release create "$TAG" {{.DIST_DIR}}/*.deb {{.DIST_DIR}}/*.rpm {{.DIST_DIR}}/*.apk {{.DIST_DIR}}/*.rsa.pub {{.DIST_DIR}}/*.tar.gz {{.DIST_DIR}}/*.zip {{.DIST_DIR}}/SHA256SUMS \
--title "$NAME" \
$( [ "{{.CHANNEL}}" = "nightly" ] && echo "--prerelease" ) \
--notes "**Version**: {{.VERSION}}" \
- || gh release upload "$TAG" {{.DIST_DIR}}/*.deb {{.DIST_DIR}}/*.rpm {{.DIST_DIR}}/*.apk {{.DIST_DIR}}/*.tar.gz {{.DIST_DIR}}/*.zip {{.DIST_DIR}}/SHA256SUMS --clobber
+ || gh release upload "$TAG" {{.DIST_DIR}}/*.deb {{.DIST_DIR}}/*.rpm {{.DIST_DIR}}/*.apk {{.DIST_DIR}}/*.rsa.pub {{.DIST_DIR}}/*.tar.gz {{.DIST_DIR}}/*.zip {{.DIST_DIR}}/SHA256SUMS --clobber
# Local dev env
devenv:config:
@@ -396,16 +442,45 @@ tasks:
deps: [web:dev, dev:server]
# Test and lint checks
- signoff:
- desc: Verify contributor identities and commit sign-offs
+ signof:
+ desc: Verifies that the author is known and has signed the CLA
cmds:
- - mise run signoff
+ - |
+ ALLOWLIST="Nikolay Govorov|github-actions\\[bot\\]"
- legal:
- desc: Verify contribution and licensing policy
- cmds:
- - task: signoff
- - mise run licenses
+ bad=0
+ for sha in $(git log --no-merges --format=%H); do
+ author=$(git log -1 --format='%an' "$sha")
+ if echo "$author" | grep -qP "^($ALLOWLIST)$"; then
+ continue
+ fi
+
+ email=$(git log -1 --format='%ae' "$sha")
+ expected="${author} <${email}>"
+ if ! git log -1 --format='%(trailers:key=Signed-off-by,valueonly)' "$sha" | grep -qF "$expected"; then
+ echo "Commit ${sha:0:8} by ${expected} is missing a valid Signed-off-by"
+ bad=1
+ fi
+ done
+
+ if [ "$bad" -eq 1 ]; then
+ echo "All commits must be signed off (git commit -s). See CLA.md"
+ exit 1
+ fi
+
+ # Check that all authors and committers are in .mailmap
+ missing=0
+ for email in $(git log --no-merges --format='%ae%n%ce' | sort -u); do
+ if ! grep -v '^#' .mailmap | grep -qF "<${email}>"; then
+ echo "Email <${email}> is not in .mailmap"
+ missing=1
+ fi
+ done
+
+ if [ "$missing" -eq 1 ]; then
+ echo "All contributors must be listed in .mailmap. See README.md"
+ exit 1
+ fi
web:lint:
desc: Lint and check the frontend
@@ -428,8 +503,10 @@ tasks:
desc: Run static checks
deps: [go:lint, web:lint]
cmds:
+ - task: signof
- task: go:lint
- task: web:lint
+ - reuse lint
test:
desc: Run all tests
@@ -438,6 +515,5 @@ tasks:
CGO_ENABLED: "1" # required for -race
cmds:
- go test -race -count=1 ./...
- - task: agent:test
# yaml-language-server: $schema=https://taskfile.dev/schema.json
diff --git a/VERSION.go b/VERSION.go
index de0fcec..959a691 100644
--- a/VERSION.go
+++ b/VERSION.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
// This package is a small hack for embedding VERSION into a project.
diff --git a/buf.yaml b/buf.yaml
index 03b39af..ddd7a3c 100644
--- a/buf.yaml
+++ b/buf.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
version: v2
diff --git a/charts/mirum/Chart.yaml b/charts/mirum/Chart.yaml
deleted file mode 100644
--- a/charts/mirum/Chart.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-apiVersion: v2
-type: application
-version: 0.1.0
-
-name: mirum
-home: https://github.com/dimidiumlabs/mirum
-description: Mirum CI server and workers
diff --git a/charts/mirum/templates/_helpers.tpl b/charts/mirum/templates/_helpers.tpl
deleted file mode 100644
--- a/charts/mirum/templates/_helpers.tpl
+++ /dev/null
@@ -1,40 +0,0 @@
-{{/* SPDX-License-Identifier: AGPL-3.0-or-later */}}
-{{/* vim: set filetype=helm: */}}
-{{- define "mirum.name" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
-{{- end -}}
-
-{{- define "mirum.fullname" -}}
-{{- if .Values.fullnameOverride -}}
-{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
-{{- else -}}
-{{- printf "%s-%s" .Release.Name (include "mirum.name" .) | trunc 63 | trimSuffix "-" -}}
-{{- end -}}
-{{- end -}}
-
-{{- define "mirum.labels" -}}
-helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
-app.kubernetes.io/name: {{ include "mirum.name" . }}
-app.kubernetes.io/instance: {{ .Release.Name }}
-app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
-app.kubernetes.io/managed-by: {{ .Release.Service }}
-{{- end -}}
-
-{{- define "mirum.componentLabels" -}}
-{{ include "mirum.labels" .root }}
-app.kubernetes.io/component: {{ .component }}
-{{- end -}}
-
-{{- define "mirum.selectorLabels" -}}
-app.kubernetes.io/name: {{ include "mirum.name" .root }}
-app.kubernetes.io/instance: {{ .root.Release.Name }}
-app.kubernetes.io/component: {{ .component }}
-{{- end -}}
-
-{{- define "mirum.image" -}}
-{{- if .image.digest -}}
-{{- printf "%s@%s" .image.repository .image.digest -}}
-{{- else -}}
-{{- printf "%s:%s" .image.repository (default .root.Chart.AppVersion .image.tag) -}}
-{{- end -}}
-{{- end -}}
diff --git a/charts/mirum/templates/server-deployment.yaml b/charts/mirum/templates/server-deployment.yaml
deleted file mode 100644
--- a/charts/mirum/templates/server-deployment.yaml
+++ /dev/null
@@ -1,142 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-{{- if and .Values.server.enabled (ne (int .Values.server.replicaCount) 1) }}
-{{- fail "mirum: server.replicaCount must be 1 while the task queue is process-local" }}
-{{- end }}
-{{- if and .Values.server.enabled .Values.server.postgresqlMtls.enabled (not .Values.server.serviceAccountName) }}
-{{- fail "mirum: server.serviceAccountName is required when server.postgresqlMtls.enabled=true" }}
-{{- end }}
-{{- if .Values.server.enabled }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: {{ include "mirum.fullname" . }}-server
- labels:
- {{- include "mirum.componentLabels" (dict "root" . "component" "server") | nindent 4 }}
-spec:
- replicas: {{ .Values.server.replicaCount }}
- strategy:
- type: Recreate
- selector:
- matchLabels:
- {{- include "mirum.selectorLabels" (dict "root" . "component" "server") | nindent 6 }}
- template:
- metadata:
- annotations:
- {{- with .Values.server.podAnnotations }}
- {{- toYaml . | nindent 8 }}
- {{- end }}
- labels:
- {{- include "mirum.selectorLabels" (dict "root" . "component" "server") | nindent 8 }}
- {{- with .Values.server.podLabels }}
- {{- toYaml . | nindent 8 }}
- {{- end }}
- spec:
- automountServiceAccountToken: false
- {{- with .Values.server.serviceAccountName }}
- serviceAccountName: {{ . | quote }}
- {{- end }}
- terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
- securityContext:
- runAsNonRoot: true
- runAsUser: 10000
- runAsGroup: 10000
- fsGroup: 10000
- fsGroupChangePolicy: OnRootMismatch
- {{- with .Values.imagePullSecrets }}
- imagePullSecrets:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- containers:
- - name: server
- image: {{ include "mirum.image" (dict "root" . "image" .Values.server.image) | quote }}
- imagePullPolicy: {{ .Values.server.image.pullPolicy }}
- args: ["daemon", "--config=/etc/mirum/secret/config.yaml"]
- securityContext:
- allowPrivilegeEscalation: false
- capabilities:
- drop: [ALL]
- readOnlyRootFilesystem: true
- ports:
- - name: web
- containerPort: {{ .Values.server.webPort }}
- - name: grpc
- containerPort: {{ .Values.server.grpcPort }}
- {{- with .Values.server.extraEnv }}
- env:
- {{- toYaml . | nindent 12 }}
- {{- end }}
- startupProbe:
- tcpSocket: { port: web }
- failureThreshold: 30
- periodSeconds: 2
- readinessProbe:
- tcpSocket: { port: web }
- livenessProbe:
- tcpSocket: { port: web }
- periodSeconds: 20
- resources:
- {{- toYaml .Values.server.resources | nindent 12 }}
- volumeMounts:
- - name: config
- mountPath: /etc/mirum/secret
- readOnly: true
- {{- with .Values.server.grpcTls.existingSecret }}
- - name: grpc-tls
- mountPath: /etc/mirum/grpc-tls
- readOnly: true
- {{- end }}
- {{- if .Values.server.postgresqlMtls.enabled }}
- - name: postgresql-tls
- mountPath: {{ .Values.server.postgresqlMtls.mountPath }}
- readOnly: true
- {{- end }}
- - name: run
- mountPath: /run/mirum-server
- volumes:
- - name: config
- secret:
- secretName: {{ .Values.server.existingSecret }}
- defaultMode: 0440
- {{- with .Values.server.grpcTls.existingSecret }}
- - name: grpc-tls
- secret:
- secretName: {{ . }}
- defaultMode: 0440
- {{- end }}
- {{- if .Values.server.postgresqlMtls.enabled }}
- - name: postgresql-tls
- csi:
- driver: csi.cert-manager.io
- readOnly: true
- volumeAttributes:
- csi.cert-manager.io/issuer-name: {{ required "mirum: server.postgresqlMtls.issuerName is required" .Values.server.postgresqlMtls.issuerName | quote }}
- csi.cert-manager.io/issuer-kind: {{ .Values.server.postgresqlMtls.issuerKind | quote }}
- csi.cert-manager.io/common-name: {{ required "mirum: server.postgresqlMtls.commonName is required" .Values.server.postgresqlMtls.commonName | quote }}
- csi.cert-manager.io/uri-sans: {{ .Values.server.postgresqlMtls.uriSan | quote }}
- csi.cert-manager.io/key-algorithm: "ECDSA"
- csi.cert-manager.io/key-size: "256"
- csi.cert-manager.io/key-encoding: "PKCS8"
- csi.cert-manager.io/key-usages: "digital signature,client auth"
- csi.cert-manager.io/duration: {{ .Values.server.postgresqlMtls.duration | quote }}
- csi.cert-manager.io/renew-before: {{ .Values.server.postgresqlMtls.renewBefore | quote }}
- csi.cert-manager.io/fs-group: {{ .Values.server.postgresqlMtls.fsGroup | quote }}
- {{- end }}
- - name: run
- emptyDir: {}
- {{- with .Values.server.nodeSelector }}
- nodeSelector:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- {{- with .Values.server.affinity }}
- affinity:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- {{- with .Values.server.tolerations }}
- tolerations:
- {{- toYaml . | nindent 8 }}
- {{- end }}
-{{- end }}
-
-# vim: set filetype=helm:
diff --git a/charts/mirum/templates/server-httproute.yaml b/charts/mirum/templates/server-httproute.yaml
deleted file mode 100644
--- a/charts/mirum/templates/server-httproute.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-{{- if and .Values.server.enabled .Values.server.route.enabled }}
-apiVersion: gateway.networking.k8s.io/v1
-kind: HTTPRoute
-metadata:
- name: {{ include "mirum.fullname" . }}
- labels:
- {{- include "mirum.componentLabels" (dict "root" . "component" "server") | nindent 4 }}
-spec:
- {{- with .Values.server.route.parentRefs }}
- parentRefs:
- {{- toYaml . | nindent 4 }}
- {{- end }}
- {{- with .Values.server.route.hostnames }}
- hostnames:
- {{- toYaml . | nindent 4 }}
- {{- end }}
- rules:
- - backendRefs:
- - name: {{ include "mirum.fullname" . }}
- port: {{ .Values.server.webPort }}
-{{- end }}
-
-# vim: set filetype=helm:
diff --git a/charts/mirum/templates/server-service.yaml b/charts/mirum/templates/server-service.yaml
deleted file mode 100644
--- a/charts/mirum/templates/server-service.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-{{- if .Values.server.enabled }}
-apiVersion: v1
-kind: Service
-metadata:
- name: {{ include "mirum.fullname" . }}
- labels:
- {{- include "mirum.componentLabels" (dict "root" . "component" "server") | nindent 4 }}
-spec:
- selector:
- {{- include "mirum.selectorLabels" (dict "root" . "component" "server") | nindent 4 }}
- ports:
- - name: web
- port: {{ .Values.server.webPort }}
- targetPort: web
- - name: grpc
- port: {{ .Values.server.grpcPort }}
- targetPort: grpc
-{{- end }}
-
-# vim: set filetype=helm:
diff --git a/charts/mirum/templates/server-tcproute.yaml b/charts/mirum/templates/server-tcproute.yaml
deleted file mode 100644
--- a/charts/mirum/templates/server-tcproute.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-{{- if and .Values.server.enabled .Values.server.grpcRoute.enabled }}
-apiVersion: gateway.networking.k8s.io/v1
-kind: TCPRoute
-metadata:
- name: {{ include "mirum.fullname" . }}-grpc
- labels:
- {{- include "mirum.componentLabels" (dict "root" . "component" "server") | nindent 4 }}
-spec:
- {{- with .Values.server.grpcRoute.parentRefs }}
- parentRefs:
- {{- toYaml . | nindent 4 }}
- {{- end }}
- rules:
- - backendRefs:
- - name: {{ include "mirum.fullname" . }}
- port: {{ .Values.server.grpcPort }}
-{{- end }}
-
-# vim: set filetype=helm:
diff --git a/charts/mirum/templates/worker-deployment.yaml b/charts/mirum/templates/worker-deployment.yaml
deleted file mode 100644
--- a/charts/mirum/templates/worker-deployment.yaml
+++ /dev/null
@@ -1,87 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-{{- if .Values.worker.enabled }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: {{ include "mirum.fullname" . }}-worker
- labels:
- {{- include "mirum.componentLabels" (dict "root" . "component" "worker") | nindent 4 }}
-spec:
- replicas: {{ .Values.worker.replicaCount }}
- selector:
- matchLabels:
- {{- include "mirum.selectorLabels" (dict "root" . "component" "worker") | nindent 6 }}
- template:
- metadata:
- annotations:
- {{- with .Values.worker.podAnnotations }}
- {{- toYaml . | nindent 8 }}
- {{- end }}
- labels:
- {{- include "mirum.selectorLabels" (dict "root" . "component" "worker") | nindent 8 }}
- {{- with .Values.worker.podLabels }}
- {{- toYaml . | nindent 8 }}
- {{- end }}
- spec:
- automountServiceAccountToken: false
- terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
- securityContext:
- runAsNonRoot: true
- runAsUser: 10000
- runAsGroup: 10000
- fsGroup: 10000
- fsGroupChangePolicy: OnRootMismatch
- {{- with .Values.imagePullSecrets }}
- imagePullSecrets:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- containers:
- - name: worker
- image: {{ include "mirum.image" (dict "root" . "image" .Values.worker.image) | quote }}
- imagePullPolicy: {{ .Values.worker.image.pullPolicy }}
- args: ["--config=/etc/mirum/secret/config.yaml"]
- securityContext:
- allowPrivilegeEscalation: false
- capabilities:
- drop: [ALL]
- readOnlyRootFilesystem: true
- {{- with .Values.worker.extraEnv }}
- env:
- {{- toYaml . | nindent 12 }}
- {{- end }}
- resources:
- {{- toYaml .Values.worker.resources | nindent 12 }}
- volumeMounts:
- - name: config
- mountPath: /etc/mirum/secret
- readOnly: true
- - name: workspace
- mountPath: /var/lib/mirum-worker
- - name: tmp
- mountPath: /tmp
- volumes:
- - name: config
- secret:
- secretName: {{ .Values.worker.existingSecret }}
- defaultMode: 0440
- - name: workspace
- emptyDir: {}
- - name: tmp
- emptyDir: {}
- {{- with .Values.worker.nodeSelector }}
- nodeSelector:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- {{- with .Values.worker.affinity }}
- affinity:
- {{- toYaml . | nindent 8 }}
- {{- end }}
- {{- with .Values.worker.tolerations }}
- tolerations:
- {{- toYaml . | nindent 8 }}
- {{- end }}
-{{- end }}
-
-# vim: set filetype=helm:
diff --git a/charts/mirum/values.yaml b/charts/mirum/values.yaml
deleted file mode 100644
--- a/charts/mirum/values.yaml
+++ /dev/null
@@ -1,75 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-imagePullSecrets: []
-nameOverride: ""
-fullnameOverride: ""
-
-server:
- enabled: true
- replicaCount: 1
- image:
- repository: ghcr.io/dimidiumlabs/mirum-server
- tag: ""
- digest: ""
- pullPolicy: IfNotPresent
- # Must contain config.yaml and any private files other than the gRPC
- # certificate. Keep database_uri, pepper, token, and webhook_secret in this
- # Secret rather than Helm values.
- existingSecret: mirum-server
- # Optional existing ServiceAccount used by the server. The default empty
- # value preserves the current Deployment and external database path.
- serviceAccountName: ""
- postgresqlMtls:
- # Opt-in client-certificate mount for passwordless PostgreSQL mTLS. Disabled
- # by default; no CSI resources or volume mounts are rendered unless enabled.
- enabled: false
- mountPath: /etc/mirum/postgresql-tls
- issuerName: ""
- issuerKind: ClusterIssuer
- commonName: ""
- uriSan: "spiffe://ddlabs.internal/ns/${POD_NAMESPACE}/sa/${SERVICE_ACCOUNT_NAME}/pod/${POD_UID}"
- duration: 2160h
- renewBefore: 360h
- fsGroup: 10000
- grpcTls:
- # Optional cert-manager-style Secret containing tls.crt and tls.key. The
- # files are mounted at /etc/mirum/grpc-tls/ for config.yaml to reference.
- existingSecret: ""
- webPort: 3000
- grpcPort: 2000
- route:
- enabled: false
- hostnames: []
- parentRefs: []
- grpcRoute:
- enabled: false
- parentRefs: []
- resources: {}
- extraEnv: []
- podAnnotations: {}
- podLabels: {}
- nodeSelector: {}
- tolerations: []
- affinity: {}
-
-worker:
- enabled: false
- replicaCount: 1
- image:
- repository: ghcr.io/dimidiumlabs/mirum-worker
- tag: ""
- digest: ""
- pullPolicy: IfNotPresent
- # Must contain config.yaml, the Ed25519 key referenced by key_file, and an
- # optional CA file referenced by tls_ca.
- existingSecret: mirum-worker
- resources: {}
- extraEnv: []
- podAnnotations: {}
- podLabels: {}
- nodeSelector: {}
- tolerations: []
- affinity: {}
-
-terminationGracePeriodSeconds: 40
diff --git a/cmd/mirum-agent/build.zig b/cmd/mirum-agent/build.zig
deleted file mode 100644
--- a/cmd/mirum-agent/build.zig
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const target = b.standardTargetOptions(.{
- .default_target = .{ .abi = .musl },
- });
- const optimize = b.standardOptimizeOption(.{});
-
- const strip = b.option(bool, "strip", "Strip debug info from the agent binary") orelse false;
-
- // Strict flags for our own C. Unity is compiled separately, without
- // them — a third-party header should not have to satisfy our lint.
- const cflags: []const []const u8 = &.{
- "-std=c99",
- "-Wall",
- "-Wextra",
- "-Wconversion",
- "-Wshadow",
- "-Wstrict-prototypes",
- };
-
- // libmirum-agent: the agent logic, compiled once and exposed only
- // through mirum-agent.h. Both the executable and the tests link it.
- const lib = b.addLibrary(.{
- .name = "mirum-agent",
- .linkage = .static,
- .root_module = b.createModule(.{
- .target = target,
- .optimize = optimize,
- .strip = strip,
- .link_libc = true,
- .link_libcpp = false,
- }),
- });
- lib.root_module.addIncludePath(b.path(""));
- lib.root_module.addCSourceFile(.{
- .file = b.path("mirum-agent.c"),
- .flags = cflags,
- });
-
- // mirum-agent: the executable entry point, links the library.
- const exe = b.addExecutable(.{
- .name = "mirum-agent",
- .root_module = b.createModule(.{
- .target = target,
- .optimize = optimize,
- .strip = strip,
- .link_libc = true,
- .link_libcpp = false,
- }),
- });
- exe.root_module.addIncludePath(b.path(""));
- exe.root_module.addCSourceFile(.{
- .file = b.path("main.c"),
- .flags = cflags,
- });
- exe.root_module.linkLibrary(lib);
-
- b.installArtifact(exe);
-
- const run_cmd = b.addRunArtifact(exe);
- run_cmd.step.dependOn(b.getInstallStep());
- if (b.args) |args| run_cmd.addArgs(args);
-
- const run_step = b.step("run", "Run the agent");
- run_step.dependOn(&run_cmd.step);
-
- // Unity: built as its own library so its sources never see our flags.
- const unity_dep = b.dependency("unity", .{});
- const unity = b.addLibrary(.{
- .name = "unity",
- .linkage = .static,
- .root_module = b.createModule(.{
- .target = target,
- .optimize = optimize,
- .link_libc = true,
- .link_libcpp = false,
- }),
- });
- unity.root_module.addIncludePath(unity_dep.path("src"));
- unity.root_module.addCSourceFile(.{
- .file = unity_dep.path("src/unity.c"),
- });
-
- // test: links libmirum-agent through its public header only — the
- // agent sources are never recompiled into the test binary. Unity's
- // headers go on the system path so they bypass our warnings.
- const exe_tests = b.addExecutable(.{
- .name = "test",
- .root_module = b.createModule(.{
- .target = target,
- .optimize = optimize,
- .link_libc = true,
- .link_libcpp = false,
- }),
- });
- exe_tests.root_module.addIncludePath(b.path(""));
- exe_tests.root_module.addSystemIncludePath(unity_dep.path("src"));
- exe_tests.root_module.addCSourceFile(.{
- .file = b.path("test.c"),
- .flags = cflags,
- });
- exe_tests.root_module.linkLibrary(lib);
- exe_tests.root_module.linkLibrary(unity);
-
- const run_exe_tests = b.addRunArtifact(exe_tests);
-
- const test_step = b.step("test", "Run tests");
- test_step.dependOn(&run_exe_tests.step);
-}
diff --git a/cmd/mirum-agent/build.zig.zon b/cmd/mirum-agent/build.zig.zon
deleted file mode 100644
--- a/cmd/mirum-agent/build.zig.zon
+++ /dev/null
@@ -1,17 +0,0 @@
-.{
- .name = .agent,
- .version = "0.0.0",
- .fingerprint = 0x268b9c9d6e24fb9b, // Changing this has security and trust implications.
- .minimum_zig_version = "0.16.0", // keep in sync with github actions
- .dependencies = .{
- .unity = .{
- .url = "https://github.com/ThrowTheSwitch/Unity/archive/refs/tags/v2.6.1.tar.gz",
- .hash = "N-V-__8AAAQfEgAjW551lOzfHoVAve6KndztgTjCXsJvElP9",
- },
- },
- .paths = .{
- "build.zig", "build.zig.zon",
- "test.c", "main.c",
- "mirum-agent.c", "mirum-agent.h",
- },
-}
diff --git a/cmd/mirum-agent/main.c b/cmd/mirum-agent/main.c
deleted file mode 100644
--- a/cmd/mirum-agent/main.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-#include "mirum-agent.h"
-
-int main(int argc, const char *argv[]) {
- mirum_init();
-
- return 0;
-}
diff --git a/cmd/mirum-agent/mirum-agent.c b/cmd/mirum-agent/mirum-agent.c
deleted file mode 100644
--- a/cmd/mirum-agent/mirum-agent.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-#include <stdio.h>
-
-#include "mirum-agent.h"
-
-void mirum_init(void) {
- printf("This is %s\n", "mirum-agent");
-}
diff --git a/cmd/mirum-agent/mirum-agent.h b/cmd/mirum-agent/mirum-agent.h
deleted file mode 100644
--- a/cmd/mirum-agent/mirum-agent.h
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-#ifndef MIRUM_AGENT_H
-#define MIRUM_AGENT_H
-
-void mirum_init(void);
-
-#endif // MIRUM_AGENT_H
diff --git a/cmd/mirum-agent/test.c b/cmd/mirum-agent/test.c
deleted file mode 100644
--- a/cmd/mirum-agent/test.c
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-#include "unity.h"
-
-#include "mirum-agent.h"
-
-void setUp(void) {}
-void tearDown(void) {}
-
-// Smoke test: the public entry point links and runs without crashing.
-// Substantive suites (TLV decode, channel state machine) land with that
-// code, exercising mirum-agent.h the same way.
-static void test_mirum_init_runs(void) {
- mirum_init();
-}
-
-int main(void) {
- UNITY_BEGIN();
- RUN_TEST(test_mirum_init_runs);
- return UNITY_END();
-}
diff --git a/cmd/mirum-server/Dockerfile b/cmd/mirum-server/Dockerfile
deleted file mode 100644
--- a/cmd/mirum-server/Dockerfile
+++ /dev/null
@@ -1,20 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-FROM gcr.io/distroless/static-debian13:nonroot@sha256:1c2c046bc09ed40fad370b599a0b1ae7987f55b01e247cf27a7c27cd97e5bbc7
-
-ARG TARGETARCH
-
-LABEL org.opencontainers.image.source="https://github.com/dimidiumlabs/mirum" \
- org.opencontainers.image.licenses="AGPL-3.0-or-later" \
- org.opencontainers.image.title="mirum-server"
-
-COPY --chown=root:root --chmod=0755 .container/mirum-server-linux-${TARGETARCH} /usr/local/bin/mirum-server
-COPY LICENSE README.md /usr/share/doc/mirum/
-
-USER 10000:10000
-EXPOSE 3000 2000
-ENTRYPOINT ["/usr/local/bin/mirum-server"]
-CMD ["daemon", "--config=/etc/mirum/config.yaml"]
-
-# syntax=docker/dockerfile:
diff --git a/cmd/mirum-server/actor.go b/cmd/mirum-server/actor.go
index 085b8e7..03d30ba 100644
--- a/cmd/mirum-server/actor.go
+++ b/cmd/mirum-server/actor.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/api_cli.go b/cmd/mirum-server/api_cli.go
index 400f337..8220c65 100644
--- a/cmd/mirum-server/api_cli.go
+++ b/cmd/mirum-server/api_cli.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
@@ -232,45 +232,6 @@ func registerMessageField(cmd *cobra.Command, fd protoreflect.FieldDescriptor, f
case "mirum.api.PageRequest":
return nil
-
- case "mirum.api.Locale":
- langFlag := flagName + "-language"
- dfFlag := flagName + "-date-format"
- flags.String(langFlag, "", "locale language (e.g. en)")
- flags.String(dfFlag, "", "date format (DMY, MDY, YMD)")
- return func(fs *pflag.FlagSet, m protoreflect.Message) error {
- lang, _ := fs.GetString(langFlag)
- dfStr, _ := fs.GetString(dfFlag)
- if lang == "" && dfStr == "" {
- return nil
- }
- loc := &apipb.Locale{}
- if lang != "" {
- loc.Language = &lang
- }
- if dfStr != "" {
- want := strings.ToUpper(dfStr)
- enumVals := loc.ProtoReflect().Descriptor().Fields().ByName("date_format").Enum().Values()
- var n protoreflect.EnumNumber
- var found bool
- for i := 0; i < enumVals.Len(); i++ {
- ev := enumVals.Get(i)
- name := string(ev.Name())
- if idx := strings.LastIndexByte(name, '_'); idx >= 0 && strings.ToUpper(name[idx+1:]) == want {
- n = ev.Number()
- found = true
- break
- }
- }
- if !found {
- return fmt.Errorf("--%s: unknown value %q (valid: DMY, MDY, YMD)", dfFlag, dfStr)
- }
- df := apipb.DateFormat(n)
- loc.DateFormat = &df
- }
- m.Set(fd, protoreflect.ValueOfMessage(loc.ProtoReflect()))
- return nil
- }
}
if required {
diff --git a/cmd/mirum-server/cert.go b/cmd/mirum-server/cert.go
index 19ee798..67128d5 100644
--- a/cmd/mirum-server/cert.go
+++ b/cmd/mirum-server/cert.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/cert_test.go b/cmd/mirum-server/cert_test.go
index 4f74f0d..794b2f6 100644
--- a/cmd/mirum-server/cert_test.go
+++ b/cmd/mirum-server/cert_test.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/config.go b/cmd/mirum-server/config.go
index 614cb3e..48d6ed2 100644
--- a/cmd/mirum-server/config.go
+++ b/cmd/mirum-server/config.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/database.go b/cmd/mirum-server/database.go
index 68356c3..27db982 100644
--- a/cmd/mirum-server/database.go
+++ b/cmd/mirum-server/database.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
@@ -32,27 +32,25 @@ import (
)
var (
- ErrAcquire = errors.New("database: failed to acquire connection")
- ErrAlreadyMember = errors.New("database: already a member")
- ErrEmailTaken = errors.New("database: email already taken")
- ErrInvalidCreds = errors.New("database: invalid credentials")
- ErrInvalidEmail = errors.New("database: invalid email")
- ErrInvalidRole = errors.New("database: invalid role")
- ErrInvalidSlug = errors.New("database: invalid slug")
- ErrInvalidDateFormat = errors.New("database: invalid date format")
- ErrInvalidTimezone = errors.New("database: invalid timezone")
- ErrLastOwner = errors.New("database: last owner")
- ErrMigrate = errors.New("database: failed to create migrator")
- ErrNotImplemented = errors.New("database: filter not implemented")
- ErrNotMember = errors.New("database: not a member")
- ErrOpen = errors.New("database: failed to open")
- ErrOrgNotFound = errors.New("database: organization not found")
- ErrPing = errors.New("database: failed to ping")
- ErrReservedEmail = errors.New("database: email uses a reserved domain")
- ErrSlugTaken = errors.New("database: slug already taken")
- ErrSoleOwner = errors.New("database: sole owner of an organization")
- ErrUserNotFound = errors.New("database: user not found")
- ErrWorkerNotFound = errors.New("database: worker not found")
+ ErrAcquire = errors.New("database: failed to acquire connection")
+ ErrAlreadyMember = errors.New("database: already a member")
+ ErrEmailTaken = errors.New("database: email already taken")
+ ErrInvalidCreds = errors.New("database: invalid credentials")
+ ErrInvalidEmail = errors.New("database: invalid email")
+ ErrInvalidRole = errors.New("database: invalid role")
+ ErrInvalidSlug = errors.New("database: invalid slug")
+ ErrLastOwner = errors.New("database: last owner")
+ ErrMigrate = errors.New("database: failed to create migrator")
+ ErrNotImplemented = errors.New("database: filter not implemented")
+ ErrNotMember = errors.New("database: not a member")
+ ErrOpen = errors.New("database: failed to open")
+ ErrOrgNotFound = errors.New("database: organization not found")
+ ErrPing = errors.New("database: failed to ping")
+ ErrReservedEmail = errors.New("database: email uses a reserved domain")
+ ErrSlugTaken = errors.New("database: slug already taken")
+ ErrSoleOwner = errors.New("database: sole owner of an organization")
+ ErrUserNotFound = errors.New("database: user not found")
+ ErrWorkerNotFound = errors.New("database: worker not found")
)
// reservedEmailSuffix is the domain carved out for synthetic actors
@@ -111,26 +109,11 @@ type DB struct {
Pool *pgxpool.Pool
}
-type DateFormat int
-
-const (
- DateFormatDMY DateFormat = 1
- DateFormatMDY DateFormat = 2
- DateFormatYMD DateFormat = 3
-)
-
-type Locale struct {
- Language *string
- DateFormat *DateFormat
-}
-
// User holds info about a user.
type User struct {
ID UserID
Email string
CreatedAt time.Time
- Locale *Locale
- Timezone *string
}
// Organization holds info about an organization.
@@ -231,21 +214,13 @@ func (db *DB) Migrate(ctx context.Context) error {
}
migrator.AppendMigration("create_users", `
- CREATE TYPE locale_settings AS (
- language TEXT,
- date_format INTEGER
- );
-
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuidv7(),
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
superuser BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- deleted_at TIMESTAMPTZ,
-
- locale locale_settings,
- timezone TEXT
+ deleted_at TIMESTAMPTZ
);
CREATE FUNCTION app_user_id() RETURNS uuid STABLE AS $$
@@ -269,8 +244,6 @@ func (db *DB) Migrate(ctx context.Context) error {
);
$$ LANGUAGE sql;
`, `
- DROP TYPE locale_settings;
- DROP TYPE date_format_t;
DROP FUNCTION app_issuper;
DROP FUNCTION app_user_id;
DROP TABLE users;
@@ -507,16 +480,12 @@ func (db *DB) UserGet(ctx context.Context, actor Actor, ref UserRef) (*User, err
col, val := ref.where()
q := sb.PostgreSQL.NewSelectBuilder()
- sql, args := q.Select("id", "email", "created_at",
- "(locale).language", "(locale).date_format", "timezone").
+ sql, args := q.Select("id", "email", "created_at").
From("users").
Where(q.Equal(col, val), q.IsNull("deleted_at")).
Build()
- u.Locale = &Locale{}
- if err := tx.QueryRow(ctx, sql, args...).Scan(
- &u.ID, &u.Email, &u.CreatedAt, &u.Locale.Language, &u.Locale.DateFormat, &u.Timezone,
- ); err != nil {
+ if err := tx.QueryRow(ctx, sql, args...).Scan(&u.ID, &u.Email, &u.CreatedAt); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrUserNotFound
}
@@ -549,8 +518,7 @@ func (db *DB) UserList(ctx context.Context, actor Actor, cursor UserID, limit in
}
q := sb.PostgreSQL.NewSelectBuilder()
- q.Select("id", "email", "created_at",
- "(locale).language", "(locale).date_format", "timezone").
+ q.Select("id", "email", "created_at").
From("users").
Where(q.IsNull("deleted_at")).
OrderBy("id").
@@ -568,8 +536,7 @@ func (db *DB) UserList(ctx context.Context, actor Actor, cursor UserID, limit in
for rows.Next() {
var u User
- u.Locale = &Locale{}
- if err := rows.Scan(&u.ID, &u.Email, &u.CreatedAt, &u.Locale.Language, &u.Locale.DateFormat, &u.Timezone); err != nil {
+ if err := rows.Scan(&u.ID, &u.Email, &u.CreatedAt); err != nil {
return err
}
users = append(users, u)
@@ -580,16 +547,13 @@ func (db *DB) UserList(ctx context.Context, actor Actor, cursor UserID, limit in
return users, total, err
}
-type UserUpdateParams struct {
- Email *string
- Password *string
- Locale *Locale
- Timezone *string
-}
-
// UserUpdate updates a user's email and/or password.
// Invalidates all sessions when password changes.
-func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, p UserUpdateParams, pepper []byte) error {
+func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, email *string, password *string, pepper []byte) error {
+ if email == nil && password == nil {
+ return nil
+ }
+
var id UserID
return db.apicall(ctx, actor,
func(tx pgx.Tx) error {
@@ -601,46 +565,24 @@ func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, p UserUp
return checkSelf(actor, id)
},
func(tx pgx.Tx) error {
- if p.Email != nil && strings.HasSuffix(strings.ToLower(*p.Email), reservedEmailSuffix) {
+ if email != nil && strings.HasSuffix(strings.ToLower(*email), reservedEmailSuffix) {
return ErrReservedEmail
}
- if p.Timezone != nil {
- if _, err := time.LoadLocation(*p.Timezone); err != nil {
- return ErrInvalidTimezone
- }
- }
return nil
},
func(tx pgx.Tx) error {
ub := sb.PostgreSQL.NewUpdateBuilder()
ub.Update("users")
- var hasSet bool
- if p.Email != nil {
- ub.SetMore(ub.Assign("email", *p.Email))
- hasSet = true
+ if email != nil {
+ ub.SetMore(ub.Assign("email", *email))
}
- if p.Password != nil {
- hash, err := hashPassword(*p.Password, pepper)
+ if password != nil {
+ hash, err := hashPassword(*password, pepper)
if err != nil {
return err
}
ub.SetMore(ub.Assign("password", hash))
- hasSet = true
- }
- if p.Locale != nil {
- ub.SetMore(fmt.Sprintf(
- "locale = ROW(COALESCE(%s, (locale).language), COALESCE(%s, (locale).date_format))::locale_settings",
- ub.Var(p.Locale.Language), ub.Var(p.Locale.DateFormat),
- ))
- hasSet = true
- }
- if p.Timezone != nil {
- ub.SetMore(ub.Assign("timezone", *p.Timezone))
- hasSet = true
- }
- if !hasSet {
- return nil
}
ub.Where(ub.Equal("id", id))
@@ -653,7 +595,7 @@ func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, p UserUp
return err
}
- if p.Password != nil {
+ if password != nil {
if _, err := tx.Exec(ctx, `DELETE FROM sessions WHERE user_id = $1`, id); err != nil {
return err
}
diff --git a/cmd/mirum-server/id.go b/cmd/mirum-server/id.go
index 8f3a330..9d50142 100644
--- a/cmd/mirum-server/id.go
+++ b/cmd/mirum-server/id.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/id_test.go b/cmd/mirum-server/id_test.go
index b69c1f4..0d06fc1 100644
--- a/cmd/mirum-server/id_test.go
+++ b/cmd/mirum-server/id_test.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/licenses.go b/cmd/mirum-server/licenses.go
index dd3770d..ca01f82 100644
--- a/cmd/mirum-server/licenses.go
+++ b/cmd/mirum-server/licenses.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/main.go b/cmd/mirum-server/main.go
index 90e85d6..549373d 100644
--- a/cmd/mirum-server/main.go
+++ b/cmd/mirum-server/main.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/proto/api.proto b/cmd/mirum-server/proto/api.proto
index f088776..16a635e 100644
--- a/cmd/mirum-server/proto/api.proto
+++ b/cmd/mirum-server/proto/api.proto
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
syntax = "proto3";
@@ -95,9 +95,6 @@ enum ErrorReason {
ERROR_REASON_INVALID_SLUG = 40;
ERROR_REASON_INVALID_ROLE = 41;
ERROR_REASON_RESERVED_EMAIL = 42;
- ERROR_REASON_INVALID_LOCALE = 43;
- ERROR_REASON_INVALID_DATE_FORMAT = 44;
- ERROR_REASON_INVALID_TIMEZONE = 45;
// Auth
ERROR_REASON_UNAUTHENTICATED = 50;
@@ -147,24 +144,10 @@ message PageResponse {
// User management
-message Locale {
- optional string language = 1;
- optional DateFormat date_format = 2;
-}
-
-enum DateFormat {
- DATE_FORMAT_UNSPECIFIED = 0;
- DATE_FORMAT_DMY = 1;
- DATE_FORMAT_MDY = 2;
- DATE_FORMAT_YMD = 3;
-}
-
message User {
bytes id = 1;
string email = 2;
google.protobuf.Timestamp created_at = 3;
- Locale locale = 4;
- optional string timezone = 5;
}
message UserCreateRequest {
@@ -195,8 +178,6 @@ message UserUpdateRequest {
UserRef user = 1 [(buf.validate.field).required = true];
optional string email = 2 [(buf.validate.field).string.email = true];
optional string password = 3 [(buf.validate.field).string.min_len = 1];
- optional Locale locale = 4;
- optional string timezone = 5;
}
message UserUpdateResponse {}
diff --git a/cmd/mirum-server/proto/buf.gen.yaml b/cmd/mirum-server/proto/buf.gen.yaml
index 52cc94d..35818cc 100644
--- a/cmd/mirum-server/proto/buf.gen.yaml
+++ b/cmd/mirum-server/proto/buf.gen.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
version: v2
diff --git a/cmd/mirum-server/server.go b/cmd/mirum-server/server.go
index 4ad6113..3201256 100644
--- a/cmd/mirum-server/server.go
+++ b/cmd/mirum-server/server.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/server_admin.go b/cmd/mirum-server/server_admin.go
index 14f499f..a79c377 100644
--- a/cmd/mirum-server/server_admin.go
+++ b/cmd/mirum-server/server_admin.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
@@ -60,8 +60,6 @@ var errSpecs = []struct {
{ErrSoleOwner, connect.CodeFailedPrecondition, apipb.ErrorReason_ERROR_REASON_SOLE_OWNER},
{ErrInvalidSlug, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_SLUG},
{ErrInvalidRole, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_ROLE},
- {ErrInvalidDateFormat, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_DATE_FORMAT},
- {ErrInvalidTimezone, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_INVALID_TIMEZONE},
{ErrReservedEmail, connect.CodeInvalidArgument, apipb.ErrorReason_ERROR_REASON_RESERVED_EMAIL},
{ErrPermissionDenied, connect.CodePermissionDenied, apipb.ErrorReason_ERROR_REASON_PERMISSION_DENIED},
{ErrUnauthenticated, connect.CodeUnauthenticated, apipb.ErrorReason_ERROR_REASON_UNAUTHENTICATED},
@@ -163,16 +161,9 @@ func pageResponse[K IDKind](items int, limit int, lastID ID[K], total int) *apip
// --- Proto converters ---
func userToProto(u User) *apipb.User {
- pb := &apipb.User{
+ return &apipb.User{
Id: u.ID.Bytes(), Email: u.Email, CreatedAt: timestamppb.New(u.CreatedAt),
- Timezone: u.Timezone,
}
- pb.Locale = &apipb.Locale{Language: u.Locale.Language}
- if u.Locale.DateFormat != nil {
- df := apipb.DateFormat(*u.Locale.DateFormat)
- pb.Locale.DateFormat = &df
- }
- return pb
}
func orgToProto(o Organization) *apipb.Org {
@@ -257,19 +248,7 @@ func (a *consoleService) UserUpdate(ctx context.Context, req *connect.Request[ap
if err != nil {
return nil, mapErr(err)
}
- p := UserUpdateParams{
- Email: req.Msg.Email,
- Password: req.Msg.Password,
- Timezone: req.Msg.Timezone,
- }
- if req.Msg.Locale != nil {
- p.Locale = &Locale{Language: req.Msg.Locale.Language}
- if req.Msg.Locale.DateFormat != nil {
- df := DateFormat(*req.Msg.Locale.DateFormat)
- p.Locale.DateFormat = &df
- }
- }
- if err := a.srv.db.UserUpdate(ctx, ActorFromContext(ctx), ref, p, []byte(a.srv.cfg.Pepper)); err != nil {
+ if err := a.srv.db.UserUpdate(ctx, ActorFromContext(ctx), ref, req.Msg.Email, req.Msg.Password, []byte(a.srv.cfg.Pepper)); err != nil {
return nil, mapErr(err)
}
return connect.NewResponse(&apipb.UserUpdateResponse{}), nil
diff --git a/cmd/mirum-server/server_grpc.go b/cmd/mirum-server/server_grpc.go
index 67e545b..a2f7cf0 100644
--- a/cmd/mirum-server/server_grpc.go
+++ b/cmd/mirum-server/server_grpc.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/server_web.go b/cmd/mirum-server/server_web.go
index 3a32f2f..c382fa1 100644
--- a/cmd/mirum-server/server_web.go
+++ b/cmd/mirum-server/server_web.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/static.go b/cmd/mirum-server/static.go
index 9f8c771..bc304ba 100644
--- a/cmd/mirum-server/static.go
+++ b/cmd/mirum-server/static.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-server/static_dev.go b/cmd/mirum-server/static_dev.go
index 4d046f5..2f6d559 100644
--- a/cmd/mirum-server/static_dev.go
+++ b/cmd/mirum-server/static_dev.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
//go:build dev
diff --git a/cmd/mirum-server/static_prod.go b/cmd/mirum-server/static_prod.go
index cd0241a..891b04e 100644
--- a/cmd/mirum-server/static_prod.go
+++ b/cmd/mirum-server/static_prod.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
//go:build !dev
diff --git a/cmd/mirum-server/web/.prettierignore b/cmd/mirum-server/web/.prettierignore
index 3a220b3..b32d4d2 100644
--- a/cmd/mirum-server/web/.prettierignore
+++ b/cmd/mirum-server/web/.prettierignore
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
/node_modules
diff --git a/cmd/mirum-server/web/api/client.ts b/cmd/mirum-server/web/api/client.ts
index bc3c3be..853b085 100644
--- a/cmd/mirum-server/web/api/client.ts
+++ b/cmd/mirum-server/web/api/client.ts
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { createClient, type Interceptor } from "@connectrpc/connect";
diff --git a/cmd/mirum-server/web/components/pages/dashboard.tsx b/cmd/mirum-server/web/components/pages/dashboard.tsx
index 79843c5..80b29d5 100644
--- a/cmd/mirum-server/web/components/pages/dashboard.tsx
+++ b/cmd/mirum-server/web/components/pages/dashboard.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { useEffect, useMemo, useState } from "react";
diff --git a/cmd/mirum-server/web/components/pages/error.tsx b/cmd/mirum-server/web/components/pages/error.tsx
index a3ab820..94fcfac 100644
--- a/cmd/mirum-server/web/components/pages/error.tsx
+++ b/cmd/mirum-server/web/components/pages/error.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { Button } from "@/components/ui/button";
diff --git a/cmd/mirum-server/web/components/pages/licenses.tsx b/cmd/mirum-server/web/components/pages/licenses.tsx
index 8f09564..3e4e4ed 100644
--- a/cmd/mirum-server/web/components/pages/licenses.tsx
+++ b/cmd/mirum-server/web/components/pages/licenses.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
diff --git a/cmd/mirum-server/web/components/pages/login.tsx b/cmd/mirum-server/web/components/pages/login.tsx
index da11fa5..a50ae5e 100644
--- a/cmd/mirum-server/web/components/pages/login.tsx
+++ b/cmd/mirum-server/web/components/pages/login.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { useState } from "react";
diff --git a/cmd/mirum-server/web/components/ui/accordion.tsx b/cmd/mirum-server/web/components/ui/accordion.tsx
index d0cc170..5061b44 100644
--- a/cmd/mirum-server/web/components/ui/accordion.tsx
+++ b/cmd/mirum-server/web/components/ui/accordion.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/components/ui/button.tsx b/cmd/mirum-server/web/components/ui/button.tsx
index c0ef769..58b8580 100644
--- a/cmd/mirum-server/web/components/ui/button.tsx
+++ b/cmd/mirum-server/web/components/ui/button.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/components/ui/card.tsx b/cmd/mirum-server/web/components/ui/card.tsx
index 467ae0d..ad3dcd3 100644
--- a/cmd/mirum-server/web/components/ui/card.tsx
+++ b/cmd/mirum-server/web/components/ui/card.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/components/ui/field.tsx b/cmd/mirum-server/web/components/ui/field.tsx
index ef12592..60271ee 100644
--- a/cmd/mirum-server/web/components/ui/field.tsx
+++ b/cmd/mirum-server/web/components/ui/field.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import { useMemo } from "react";
diff --git a/cmd/mirum-server/web/components/ui/input.tsx b/cmd/mirum-server/web/components/ui/input.tsx
index 323a15a..027f64f 100644
--- a/cmd/mirum-server/web/components/ui/input.tsx
+++ b/cmd/mirum-server/web/components/ui/input.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/components/ui/label.tsx b/cmd/mirum-server/web/components/ui/label.tsx
index a09587f..423c902 100644
--- a/cmd/mirum-server/web/components/ui/label.tsx
+++ b/cmd/mirum-server/web/components/ui/label.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/components/ui/separator.tsx b/cmd/mirum-server/web/components/ui/separator.tsx
index 3a5b378..fbf8144 100644
--- a/cmd/mirum-server/web/components/ui/separator.tsx
+++ b/cmd/mirum-server/web/components/ui/separator.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2023 shadcn
+// Copyright (c) 2023 shadcn
// SPDX-License-Identifier: MIT
import * as React from "react";
diff --git a/cmd/mirum-server/web/entries/dashboard.tsx b/cmd/mirum-server/web/entries/dashboard.tsx
index 772a56b..4439aa0 100644
--- a/cmd/mirum-server/web/entries/dashboard.tsx
+++ b/cmd/mirum-server/web/entries/dashboard.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import "@/index.css";
diff --git a/cmd/mirum-server/web/entries/error.tsx b/cmd/mirum-server/web/entries/error.tsx
index 68dd86e..f2da7dd 100644
--- a/cmd/mirum-server/web/entries/error.tsx
+++ b/cmd/mirum-server/web/entries/error.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import "@/index.css";
diff --git a/cmd/mirum-server/web/entries/licenses.tsx b/cmd/mirum-server/web/entries/licenses.tsx
index 9c8956b..574d20f 100644
--- a/cmd/mirum-server/web/entries/licenses.tsx
+++ b/cmd/mirum-server/web/entries/licenses.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import "@/index.css";
diff --git a/cmd/mirum-server/web/entries/login.tsx b/cmd/mirum-server/web/entries/login.tsx
index 5ab044f..4912c97 100644
--- a/cmd/mirum-server/web/entries/login.tsx
+++ b/cmd/mirum-server/web/entries/login.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import "@/index.css";
diff --git a/cmd/mirum-server/web/eslint.config.js b/cmd/mirum-server/web/eslint.config.js
index a784504..02d5391 100644
--- a/cmd/mirum-server/web/eslint.config.js
+++ b/cmd/mirum-server/web/eslint.config.js
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import eslint from "@eslint/js";
diff --git a/cmd/mirum-server/web/index.css b/cmd/mirum-server/web/index.css
index 695bc72..1dfcc71 100644
--- a/cmd/mirum-server/web/index.css
+++ b/cmd/mirum-server/web/index.css
@@ -1,4 +1,4 @@
-/* SPDX-FileCopyrightText: 2026 Nikolay Govorov
+/* Copyright (c) 2026 Nikolay Govorov
* SPDX-License-Identifier: AGPL-3.0-or-later */
@import "tailwindcss";
diff --git a/cmd/mirum-server/web/lib/errors.ts b/cmd/mirum-server/web/lib/errors.ts
index 0fc3fb9..c9ec786 100644
--- a/cmd/mirum-server/web/lib/errors.ts
+++ b/cmd/mirum-server/web/lib/errors.ts
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { Code, ConnectError } from "@connectrpc/connect";
@@ -47,10 +47,6 @@ export function textForReason(reason: ErrorReason): string {
return "Invalid slug. Use lowercase letters, digits, and hyphens.";
case ErrorReason.INVALID_ROLE:
return "Invalid role.";
- case ErrorReason.INVALID_DATE_FORMAT:
- return "Invalid date format.";
- case ErrorReason.INVALID_TIMEZONE:
- return "Invalid timezone.";
case ErrorReason.RESERVED_EMAIL:
return "This email domain is reserved. Please use a different address.";
case ErrorReason.UNAUTHENTICATED:
diff --git a/cmd/mirum-server/web/lib/mount.tsx b/cmd/mirum-server/web/lib/mount.tsx
index cbbb46a..9cc7d6c 100644
--- a/cmd/mirum-server/web/lib/mount.tsx
+++ b/cmd/mirum-server/web/lib/mount.tsx
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { StrictMode, type ComponentType } from "react";
diff --git a/cmd/mirum-server/web/lib/utils.ts b/cmd/mirum-server/web/lib/utils.ts
index 29d1a05..6d76a21 100644
--- a/cmd/mirum-server/web/lib/utils.ts
+++ b/cmd/mirum-server/web/lib/utils.ts
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { twMerge } from "tailwind-merge";
diff --git a/cmd/mirum-server/web/shadcn.css b/cmd/mirum-server/web/shadcn.css
index 1260127..e5fddbc 100644
--- a/cmd/mirum-server/web/shadcn.css
+++ b/cmd/mirum-server/web/shadcn.css
@@ -1,4 +1,4 @@
-/* SPDX-FileCopyrightText: 2023 shadcn
+/* Copyright (c) 2023 shadcn
* SPDX-License-Identifier: MIT
*
* Inlined from `shadcn/tailwind.css` (npm package `shadcn`, MIT). Kept in
diff --git a/cmd/mirum-server/web/shell.html b/cmd/mirum-server/web/shell.html
index aec9d4b..6c35785 100644
--- a/cmd/mirum-server/web/shell.html
+++ b/cmd/mirum-server/web/shell.html
@@ -1,5 +1,5 @@
<!--
-SPDX-FileCopyrightText: 2026 Nikolay Govorov
+Copyright (c) 2026 Nikolay Govorov
SPDX-License-Identifier: AGPL-3.0-or-later
-->
diff --git a/cmd/mirum-server/web/tsconfig.json b/cmd/mirum-server/web/tsconfig.json
index bd3a03c..6dd27c0 100644
--- a/cmd/mirum-server/web/tsconfig.json
+++ b/cmd/mirum-server/web/tsconfig.json
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
{
diff --git a/cmd/mirum-server/web/vite.config.ts b/cmd/mirum-server/web/vite.config.ts
index c745ed2..ae0bf51 100644
--- a/cmd/mirum-server/web/vite.config.ts
+++ b/cmd/mirum-server/web/vite.config.ts
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
import { readdirSync } from "node:fs";
diff --git a/cmd/mirum-worker/Dockerfile b/cmd/mirum-worker/Dockerfile
deleted file mode 100644
--- a/cmd/mirum-worker/Dockerfile
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-FROM docker.io/library/alpine:3.23.5@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40
-
-ARG TARGETARCH
-
-LABEL org.opencontainers.image.source="https://github.com/dimidiumlabs/mirum" \
- org.opencontainers.image.licenses="AGPL-3.0-or-later" \
- org.opencontainers.image.title="mirum-worker"
-
-RUN apk add --no-cache bash ca-certificates git && \
- addgroup -g 10000 mirum-worker && \
- adduser -D -H -u 10000 -G mirum-worker -h /var/lib/mirum-worker \
- -s /sbin/nologin mirum-worker && \
- install -d -o mirum-worker -g mirum-worker -m 0750 /var/lib/mirum-worker
-
-COPY --chown=root:root --chmod=0755 .container/mirum-worker-linux-${TARGETARCH} /usr/local/bin/mirum-worker
-COPY LICENSE README.md /usr/share/doc/mirum/
-
-USER 10000:10000
-WORKDIR /var/lib/mirum-worker
-ENTRYPOINT ["/usr/local/bin/mirum-worker"]
-CMD ["--config=/etc/mirum/config.yaml"]
-
-# syntax=docker/dockerfile:
diff --git a/cmd/mirum-worker/client.go b/cmd/mirum-worker/client.go
index 622105f..1525904 100644
--- a/cmd/mirum-worker/client.go
+++ b/cmd/mirum-worker/client.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
@@ -19,8 +19,6 @@ import (
"dimidiumlabs/mirum/internal/protocol"
"dimidiumlabs/mirum/internal/protocol/wirepb"
"dimidiumlabs/mirum/internal/protocol/wirepb/wirepbconnect"
-
- _ "dimidiumlabs/mirum/internal/executor/host" // registers the host Runtime backend
)
type client struct {
@@ -100,7 +98,7 @@ func (c *client) work(ctx context.Context) error {
task := resp.Msg
slog.Info("task received", "id", task.Id, "repo", task.RepoFullName)
- execErr := executor.Run(ctx, task.CloneUrl, task.Branch)
+ execErr := executor.Run(task.CloneUrl, task.Branch)
result := &wirepb.TaskResult{TaskId: task.Id, Success: execErr == nil}
if execErr != nil {
diff --git a/cmd/mirum-worker/config.go b/cmd/mirum-worker/config.go
index 41a9c8c..366fdf4 100644
--- a/cmd/mirum-worker/config.go
+++ b/cmd/mirum-worker/config.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum-worker/main.go b/cmd/mirum-worker/main.go
index 3641736..e2132b5 100644
--- a/cmd/mirum-worker/main.go
+++ b/cmd/mirum-worker/main.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/cmd/mirum/main.go b/cmd/mirum/main.go
index 087bf1e..35b53ca 100644
--- a/cmd/mirum/main.go
+++ b/cmd/mirum/main.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
// Command mirum is the developer-facing CLI for the Mirum CI system.
diff --git a/go.mod b/go.mod
index 2dbba9d..d98b7ad 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module dimidiumlabs/mirum
-go 1.26.5
+go 1.26.2
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1
@@ -14,7 +14,7 @@ require (
github.com/google/uuid v1.6.0
github.com/huandu/go-sqlbuilder v1.40.1
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6
- github.com/jackc/pgx/v5 v5.9.2
+ github.com/jackc/pgx/v5 v5.9.1
github.com/jackc/tern/v2 v2.3.6
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
@@ -236,12 +236,12 @@ require (
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
- golang.org/x/mod v0.37.0 // indirect
- golang.org/x/sync v0.21.0 // indirect
- golang.org/x/sys v0.46.0 // indirect
- golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 // indirect
- golang.org/x/text v0.39.0 // indirect
- golang.org/x/tools v0.47.0 // indirect
+ golang.org/x/mod v0.34.0 // indirect
+ golang.org/x/sync v0.20.0 // indirect
+ golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c // indirect
+ golang.org/x/text v0.35.0 // indirect
+ golang.org/x/tools v0.43.0 // indirect
golang.org/x/vuln v1.1.4 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
diff --git a/go.sum b/go.sum
index c33eaa2..f4b3393 100644
--- a/go.sum
+++ b/go.sum
@@ -397,8 +397,6 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
-github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
-github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jackc/tern/v2 v2.3.6 h1:sqBIZ/CBtfMLz7zdUof0N6cVUBRVGBZ7S+F2OdCp9XU=
@@ -764,8 +762,6 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
-golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
-golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -806,7 +802,6 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
-golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -830,8 +825,6 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
-golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -884,12 +877,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
-golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc=
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
-golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc=
-golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -908,8 +897,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
-golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -965,8 +952,6 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
-golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
-golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
diff --git a/internal/config/config.go b/internal/config/config.go
index 74f0ce8..3d1256c 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
// Package config holds hard-coded tunables shared across mirum-server and mirum-worker:
diff --git a/internal/executor/environment.go b/internal/executor/environment.go
deleted file mode 100644
--- a/internal/executor/environment.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-package executor
-
-import (
- "context"
- "fmt"
- "os"
-
- "go.starlark.net/starlark"
-)
-
-// SOTaskCtx is the `ctx` value passed to the Starlark project() function. It
-// exposes the task's Runtime to the build script.
-type SOTaskCtx struct {
- ctx context.Context
- rt Runtime
-}
-
-var _ starlark.HasAttrs = (*SOTaskCtx)(nil)
-
-func (c *SOTaskCtx) Type() string { return "ctx" }
-func (c *SOTaskCtx) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: ctx") }
-func (c *SOTaskCtx) Truth() starlark.Bool { return true }
-func (c *SOTaskCtx) String() string { return "ctx" }
-
-func (c *SOTaskCtx) Freeze() {}
-
-func (c *SOTaskCtx) AttrNames() []string { return []string{"shell"} }
-func (c *SOTaskCtx) Attr(name string) (starlark.Value, error) {
- switch name {
- case "shell":
- return starlark.NewBuiltin("ctx.shell", c.shell), nil
- }
- return nil, nil
-}
-
-func (c *SOTaskCtx) shell(
- thread *starlark.Thread,
- fn *starlark.Builtin,
- args starlark.Tuple,
- kwargs []starlark.Tuple,
-) (starlark.Value, error) {
- var cmd string
- if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &cmd); err != nil {
- return nil, err
- }
-
- res, err := c.rt.Exec(c.ctx, Command{
- Args: []string{"bash", "-c", cmd},
- Stdout: os.Stdout,
- Stderr: os.Stderr,
- })
- if err != nil {
- return nil, err
- }
- if res.Code != 0 {
- return nil, fmt.Errorf("shell: %q exited with code %d", cmd, res.Code)
- }
-
- return starlark.None, nil
-}
diff --git a/internal/executor/executor.go b/internal/executor/executor.go
index d97367f..7ce8770 100644
--- a/internal/executor/executor.go
+++ b/internal/executor/executor.go
@@ -1,14 +1,15 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
-// Runtime executing the Starlark pipeline in one of the supported runtimes
package executor
import (
"bytes"
- "context"
"fmt"
"log/slog"
+ "os"
+ "os/exec"
+ "path/filepath"
"go.starlark.net/starlark"
"go.starlark.net/syntax"
@@ -16,39 +17,85 @@ import (
const entry = ".mirum/project.star"
-// Run selects a Runtime, clones the repository into it, runs the Starlark
-// build script, and discards the Runtime afterwards.
-func Run(ctx context.Context, cloneURL, branch string) error {
- rt, err := NewRuntime()
+func RunCmd(dir, name string, args ...string) (string, error) {
+ cmd := exec.Command(name, args...)
+ cmd.Dir = dir
+
+ var buf bytes.Buffer
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+
+ err := cmd.Run()
+ return buf.String(), err
+}
+
+type SOTaskCtx struct {
+ dir string
+}
+
+var _ starlark.HasAttrs = (*SOTaskCtx)(nil)
+
+func (c *SOTaskCtx) String() string { return "ctx" }
+func (c *SOTaskCtx) Type() string { return "ctx" }
+func (c *SOTaskCtx) Freeze() {}
+func (c *SOTaskCtx) Truth() starlark.Bool { return true }
+func (c *SOTaskCtx) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: ctx") }
+func (c *SOTaskCtx) AttrNames() []string { return []string{"shell"} }
+
+func (c *SOTaskCtx) Attr(name string) (starlark.Value, error) {
+ switch name {
+ case "shell":
+ return starlark.NewBuiltin("ctx.shell", c.shell), nil
+ }
+ return nil, nil
+}
+
+func (c *SOTaskCtx) shell(
+ thread *starlark.Thread,
+ fn *starlark.Builtin,
+ args starlark.Tuple,
+ kwargs []starlark.Tuple,
+) (starlark.Value, error) {
+ var cmd string
+ if err := starlark.UnpackPositionalArgs(fn.Name(), args, kwargs, 1, &cmd); err != nil {
+ return nil, err
+ }
+
+ proc := exec.Command("bash", "-c", cmd)
+ proc.Dir = c.dir
+ proc.Stdout = os.Stdout
+ proc.Stderr = os.Stderr
+ err := proc.Run()
if err != nil {
- return fmt.Errorf("create runtime: %w", err)
+ return nil, err
+ }
+
+ return starlark.None, nil
+}
+
+// Run clones the repository into a temporary directory, runs the Starlark
+// build script, and cleans up afterwards.
+func Run(cloneURL, branch string) error {
+ dir, err := os.MkdirTemp("", "mirum-*")
+ if err != nil {
+ return fmt.Errorf("create workdir: %w", err)
}
defer func() {
- if err := rt.Close(); err != nil {
- slog.Warn("executor: runtime cleanup failed", "err", err)
+ if err := os.RemoveAll(dir); err != nil {
+ slog.Warn("executor: cleanup failed", "dir", dir, "err", err)
}
}()
- var clone bytes.Buffer
- res, err := rt.Exec(ctx, Command{
- Args: []string{"git", "clone", "--depth=1", "--branch", branch, cloneURL, "."},
- Stdout: &clone,
- Stderr: &clone,
- })
- if err != nil {
- return fmt.Errorf("git clone: %w", err)
- }
- if res.Code != 0 {
- return fmt.Errorf("git clone exited with code %d: %s", res.Code, clone.String())
+ if out, err := RunCmd(dir, "git", "clone", "--depth=1", "--branch", branch, cloneURL, "."); err != nil {
+ return fmt.Errorf("git clone: %s: %w", out, err)
}
- var script bytes.Buffer
- if err := rt.FileRecv(ctx, entry, &script); err != nil {
- return fmt.Errorf("read %s: %w", entry, err)
- }
+ return runStarlark(dir)
+}
+func runStarlark(dir string) error {
thread := &starlark.Thread{Name: "mirum"}
- globals, err := starlark.ExecFileOptions(&syntax.FileOptions{}, thread, entry, script.Bytes(), nil)
+ globals, err := starlark.ExecFileOptions(&syntax.FileOptions{}, thread, filepath.Join(dir, entry), nil, nil)
if err != nil {
return err
}
@@ -62,7 +109,7 @@ func Run(ctx context.Context, cloneURL, branch string) error {
return fmt.Errorf("%s: project is not a function", entry)
}
- tctx := &SOTaskCtx{ctx: ctx, rt: rt}
- _, err = starlark.Call(thread, fn, starlark.Tuple{tctx}, nil)
+ ctx := &SOTaskCtx{dir: dir}
+ _, err = starlark.Call(thread, fn, starlark.Tuple{ctx}, nil)
return err
}
diff --git a/internal/executor/host/host.go b/internal/executor/host/host.go
deleted file mode 100644
--- a/internal/executor/host/host.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-// Package host implements an executor.Runtime that runs task commands
-// directly on the worker. It provides no isolation — it is the fast path
-// for local iteration (mirum task) and trusted workloads.
-package host
-
-import (
- "context"
- "fmt"
- "io"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "time"
-
- "dimidiumlabs/mirum/internal/executor"
-)
-
-// Host is an executor.Runtime backed by a temporary directory on the worker.
-// Commands run as child processes of the worker, with no sandboxing.
-type Host struct {
- root string
-}
-
-var _ executor.Runtime = (*Host)(nil)
-
-// New creates a Host runtime rooted at a fresh temporary directory.
-func New() (*Host, error) {
- root, err := os.MkdirTemp("", "mirum-host-*")
- if err != nil {
- return nil, fmt.Errorf("create work dir: %w", err)
- }
- return &Host{root: root}, nil
-}
-
-func init() {
- executor.RegisterRuntime(executor.RuntimeBackend{
- Name: "host",
- Priority: executor.PriorityHost,
- New: func() (executor.Runtime, error) { return New() },
- })
-}
-
-// Platform reports the worker's own OS and architecture.
-func (h *Host) Platform() executor.Platform {
- return executor.Platform{OS: runtime.GOOS, Arch: runtime.GOARCH}
-}
-
-// resolve maps an env-relative slash path to an absolute path inside root,
-// rejecting paths that would escape it.
-func (h *Host) resolve(p string) (string, error) {
- if p == "" {
- return h.root, nil
- }
- local, err := filepath.Localize(p)
- if err != nil {
- return "", fmt.Errorf("invalid path %q: %w", p, err)
- }
- return filepath.Join(h.root, local), nil
-}
-
-// Exec runs cmd as a child process of the worker.
-func (h *Host) Exec(ctx context.Context, cmd executor.Command) (executor.Result, error) {
- if len(cmd.Args) == 0 {
- return executor.Result{}, fmt.Errorf("exec: empty args")
- }
-
- dir, err := h.resolve(cmd.Dir)
- if err != nil {
- return executor.Result{}, err
- }
-
- c := exec.CommandContext(ctx, cmd.Args[0], cmd.Args[1:]...)
- c.Dir = dir
- c.Stdin = cmd.Stdin
- c.Stdout = cmd.Stdout
- c.Stderr = cmd.Stderr
- if len(cmd.Env) > 0 {
- c.Env = os.Environ()
- for k, v := range cmd.Env {
- c.Env = append(c.Env, k+"="+v)
- }
- }
-
- start := time.Now()
- runErr := c.Run()
- wall := time.Since(start)
-
- if ctx.Err() != nil {
- return executor.Result{}, ctx.Err()
- }
-
- ps := c.ProcessState
- if ps == nil {
- // The process never started, e.g. the executable was not found.
- return executor.Result{}, fmt.Errorf("exec %s: %w", cmd.Args[0], runErr)
- }
-
- return executor.Result{
- Code: ps.ExitCode(),
- Usage: executor.Usage{
- Wall: wall,
- CPUUser: ps.UserTime(),
- CPUSystem: ps.SystemTime(),
- MaxRSS: maxRSS(ps),
- },
- }, nil
-}
-
-// FileSend writes a single file into the work directory, creating parent
-// directories as needed.
-func (h *Host) FileSend(ctx context.Context, path string, r io.Reader) error {
- if err := ctx.Err(); err != nil {
- return err
- }
- dst, err := h.resolve(path)
- if err != nil {
- return err
- }
- if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
- return err
- }
- f, err := os.Create(dst)
- if err != nil {
- return err
- }
- _, copyErr := io.Copy(f, r)
- closeErr := f.Close()
- if copyErr != nil {
- return copyErr
- }
- return closeErr
-}
-
-// FileRecv streams a single file out of the work directory.
-func (h *Host) FileRecv(ctx context.Context, path string, w io.Writer) error {
- if err := ctx.Err(); err != nil {
- return err
- }
- src, err := h.resolve(path)
- if err != nil {
- return err
- }
- f, err := os.Open(src)
- if err != nil {
- return err
- }
- defer func() { _ = f.Close() }()
- _, err = io.Copy(w, f)
- return err
-}
-
-// FileList returns the env-relative paths of every file under dir.
-func (h *Host) FileList(ctx context.Context, dir string) ([]string, error) {
- base, err := h.resolve(dir)
- if err != nil {
- return nil, err
- }
-
- var out []string
- err = filepath.WalkDir(base, func(p string, d os.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if err := ctx.Err(); err != nil {
- return err
- }
- if d.IsDir() {
- return nil
- }
- rel, err := filepath.Rel(h.root, p)
- if err != nil {
- return err
- }
- out = append(out, filepath.ToSlash(rel))
- return nil
- })
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// Close removes the work directory.
-func (h *Host) Close() error {
- return os.RemoveAll(h.root)
-}
diff --git a/internal/executor/host/maxrss_other.go b/internal/executor/host/maxrss_other.go
deleted file mode 100644
--- a/internal/executor/host/maxrss_other.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-//go:build !unix
-
-package host
-
-import "os"
-
-// maxRSS returns 0 on platforms without POSIX getrusage.
-func maxRSS(*os.ProcessState) int64 { return 0 }
diff --git a/internal/executor/host/maxrss_unix.go b/internal/executor/host/maxrss_unix.go
deleted file mode 100644
--- a/internal/executor/host/maxrss_unix.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-//go:build unix
-
-package host
-
-import (
- "os"
- "runtime"
- "syscall"
-)
-
-// maxRSS extracts the peak resident set size of a finished process,
-// normalized to bytes. POSIX getrusage reports ru_maxrss in kilobytes on
-// Linux and the BSDs, but in bytes on macOS.
-func maxRSS(ps *os.ProcessState) int64 {
- ru, ok := ps.SysUsage().(*syscall.Rusage)
- if !ok {
- return 0
- }
- rss := int64(ru.Maxrss)
- if runtime.GOOS != "darwin" {
- rss *= 1024
- }
- return rss
-}
diff --git a/internal/executor/runtime.go b/internal/executor/runtime.go
deleted file mode 100644
--- a/internal/executor/runtime.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-package executor
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "log/slog"
- "sort"
- "time"
-)
-
-// Platform identifies the OS and architecture of a Runtime, as observed by
-// the task itself — the values behind tctx.os and tctx.arch.
-type Platform struct {
- OS string // "linux", "darwin", "windows", ...
- Arch string // "amd64", "arm64", "riscv64", ...
-}
-
-// Command is a single process to run inside a Runtime.
-type Command struct {
- Args []string // argv; Args[0] is the executable
- Dir string // working directory, relative to the env root
- Env map[string]string // variables added to the environment's own
- Stdin io.Reader // standard input; nil means none
- Stdout io.Writer // streamed as produced; nil means discard
- Stderr io.Writer // streamed as produced; nil means discard
-}
-
-// Usage reports the resources a Command consumed. It is collected from
-// POSIX rusage — os.ProcessState.SysUsage on the Host runtime, wait4 inside
-// the VM for the Qemu runtime — and feeds log output and billing metrics. A
-// backend leaves a field zero when it cannot measure it.
-type Usage struct {
- Wall time.Duration // wall-clock time from start to exit
- CPUUser time.Duration // CPU time spent in user mode
- CPUSystem time.Duration // CPU time spent in kernel mode
- MaxRSS int64 // peak resident set size, in bytes
-}
-
-// Result is the outcome of a Command that ran to completion.
-type Result struct {
- Code int // process exit code; 0 means success
- Usage Usage // resources the command consumed
-}
-
-// Runtime is an isolation backend: the environment in which one task's
-// commands run. The executor drives every Runtime identically and is
-// unaware of how isolation is achieved. A Runtime hosts one task and is
-// then discarded.
-type Runtime interface {
- // Platform reports the environment's OS and architecture.
- Platform() Platform
-
- // Exec runs cmd to completion. The error covers failures to start or
- // communicate with the process; a process that runs and exits non-zero
- // is a successful Exec with a non-zero Result.Code.
- Exec(ctx context.Context, cmd Command) (Result, error)
-
- // List returns the env-relative paths of the files under dir, so the
- // executor can resolve upload globs and walk a source tree.
- FileList(ctx context.Context, dir string) ([]string, error)
-
- // Send streams a single file into the environment at the env-relative
- // path, creating parent directories as needed. Bytes flow straight from
- // r — the worker never stages them on its own filesystem.
- FileSend(ctx context.Context, path string, r io.Reader) error
-
- // Recv streams a single file out of the environment to w. Like Send,
- // nothing is staged on the worker's filesystem.
- FileRecv(ctx context.Context, path string, w io.Writer) error
-
- // Close discards the environment and releases its resources.
- Close() error
-}
-
-// Selection priorities for RuntimeBackend.Priority; NewRuntime prefers higher.
-const (
- PriorityHost = 0 // unisolated, runs on the worker itself
- PriorityVM = 100 // hardware-isolated guest (QEMU)
-)
-
-// A RuntimeBackend describes one Runtime implementation. Backends register
-// themselves with RegisterRuntime, so the executor never imports the backend
-// packages directly.
-type RuntimeBackend struct {
- Name string
- Priority int
-
- // New builds a fresh Runtime for one task. A nil Runtime with nil error
- // means the backend does not apply on this worker, so NewRuntime falls
- // through to the next.
- New func() (Runtime, error)
-}
-
-// runtimeBackends is kept sorted by descending priority.
-var runtimeBackends []RuntimeBackend
-
-// RegisterRuntime adds a Runtime backend. Backends call it from an init
-// function.
-func RegisterRuntime(b RuntimeBackend) {
- runtimeBackends = append(runtimeBackends, b)
- sort.SliceStable(runtimeBackends, func(i, j int) bool {
- return runtimeBackends[i].Priority > runtimeBackends[j].Priority
- })
-}
-
-// NewRuntime builds a fresh Runtime for one task, choosing the highest-priority
-// backend that applies. The caller must Close the returned Runtime.
-func NewRuntime() (Runtime, error) {
- for _, b := range runtimeBackends {
- rt, err := b.New()
- if err != nil {
- return nil, fmt.Errorf("runtime %s: %w", b.Name, err)
- }
- if rt != nil {
- slog.Info("executor: runtime selected", "backend", b.Name)
- return rt, nil
- }
- }
- return nil, errors.New("executor: no applicable runtime backend")
-}
diff --git a/internal/forges/forge.go b/internal/forges/forge.go
index 0034046..60f01bd 100644
--- a/internal/forges/forge.go
+++ b/internal/forges/forge.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package forges
diff --git a/internal/forges/github.go b/internal/forges/github.go
index ba9973f..77bea14 100644
--- a/internal/forges/github.go
+++ b/internal/forges/github.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package forges
diff --git a/internal/protocol/backoff.go b/internal/protocol/backoff.go
index f728bbb..5a3cf20 100644
--- a/internal/protocol/backoff.go
+++ b/internal/protocol/backoff.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/backoff_test.go b/internal/protocol/backoff_test.go
index e8f42c8..92603a1 100644
--- a/internal/protocol/backoff_test.go
+++ b/internal/protocol/backoff_test.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/handshake.go b/internal/protocol/handshake.go
index 0ad164a..204600a 100644
--- a/internal/protocol/handshake.go
+++ b/internal/protocol/handshake.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/handshake_test.go b/internal/protocol/handshake_test.go
index 84eb5b4..513d7d5 100644
--- a/internal/protocol/handshake_test.go
+++ b/internal/protocol/handshake_test.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/platform.go b/internal/protocol/platform.go
index 5f66b27..d48da44 100644
--- a/internal/protocol/platform.go
+++ b/internal/protocol/platform.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/platform_test.go b/internal/protocol/platform_test.go
index e886b26..276f335 100644
--- a/internal/protocol/platform_test.go
+++ b/internal/protocol/platform_test.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package protocol
diff --git a/internal/protocol/proto/buf.gen.yaml b/internal/protocol/proto/buf.gen.yaml
index 92df46f..8db3d92 100644
--- a/internal/protocol/proto/buf.gen.yaml
+++ b/internal/protocol/proto/buf.gen.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
version: v2
diff --git a/internal/protocol/proto/wire.proto b/internal/protocol/proto/wire.proto
index 2d2b245..73668e7 100644
--- a/internal/protocol/proto/wire.proto
+++ b/internal/protocol/proto/wire.proto
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
syntax = "proto3";
diff --git a/internal/supervisor/supervisor.go b/internal/supervisor/supervisor.go
index dcacaa1..7e32cee 100644
--- a/internal/supervisor/supervisor.go
+++ b/internal/supervisor/supervisor.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
// Package supervisor provides a platform-agnostic interface for process
diff --git a/internal/supervisor/systemd.go b/internal/supervisor/systemd.go
index d95bd7f..ac57659 100644
--- a/internal/supervisor/systemd.go
+++ b/internal/supervisor/systemd.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
//go:build linux
diff --git a/mise.lock b/mise.lock
deleted file mode 100644
--- a/mise.lock
+++ /dev/null
@@ -1,84 +0,0 @@
-# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html
-
-[[tools.buf]]
-version = "1.67.0"
-backend = "aqua:bufbuild/buf"
-
-[tools.buf."platforms.linux-arm64"]
-checksum = "sha256:3d542f0f99159c8b6442f4c1f6d8f59fd669356b97aa6f6b67ea4a5af7f81ec7"
-url = "https://github.com/bufbuild/buf/releases/download/v1.67.0/buf-Linux-aarch64.tar.gz"
-url_api = "https://api.github.com/repos/bufbuild/buf/releases/assets/386412995"
-provenance = "minisign"
-
-[tools.buf."platforms.linux-x64"]
-checksum = "sha256:b8682c2ea6f377cf1441e4fbc54ed1ecccbb9f2d887560c4320cf7807f015519"
-url = "https://github.com/bufbuild/buf/releases/download/v1.67.0/buf-Linux-x86_64.tar.gz"
-url_api = "https://api.github.com/repos/bufbuild/buf/releases/assets/386413039"
-provenance = "minisign"
-
-[[tools.gh]]
-version = "2.96.0"
-backend = "aqua:cli/cli"
-
-[tools.gh."platforms.linux-arm64"]
-checksum = "sha256:06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909"
-url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_arm64.tar.gz"
-url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728549"
-provenance = "github-attestations"
-
-[tools.gh."platforms.linux-x64"]
-checksum = "sha256:83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60"
-url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_amd64.tar.gz"
-url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728543"
-provenance = "github-attestations"
-
-[[tools.go]]
-version = "1.26.6"
-backend = "core:go"
-
-[tools.go."platforms.linux-arm64"]
-checksum = "sha256:d0507e9e9d7fe012aae570108cbd76c15de879e17130ab8cb90d4d7445cb1f2e"
-url = "https://dl.google.com/go/go1.26.6.linux-arm64.tar.gz"
-
-[tools.go."platforms.linux-x64"]
-checksum = "sha256:708effb774be8237570d0add163225abbdfaf4fca28b2611df167beba4feef89"
-url = "https://dl.google.com/go/go1.26.6.linux-amd64.tar.gz"
-
-[[tools.node]]
-version = "24.18.0"
-backend = "core:node"
-
-[tools.node."platforms.linux-arm64"]
-checksum = "sha256:6b4484c2190274175df9aa8f28e2d758a819cb1c1fe6ab481e2f95b463ab8508"
-url = "https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-arm64.tar.gz"
-
-[tools.node."platforms.linux-x64"]
-checksum = "sha256:783130984963db7ba9cbd01089eaf2c2efb055c7c1693c943174b967b3050cb8"
-url = "https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.gz"
-
-[[tools.task]]
-version = "3.52.0"
-backend = "aqua:go-task/task"
-
-[tools.task."platforms.linux-arm64"]
-checksum = "sha256:7e0044108830cec0534577b289564e3b7c83e6df276feb631a1edc63d04e4ebe"
-url = "https://github.com/go-task/task/releases/download/v3.52.0/task_linux_arm64.tar.gz"
-url_api = "https://api.github.com/repos/go-task/task/releases/assets/464613352"
-
-[tools.task."platforms.linux-x64"]
-checksum = "sha256:02c679ffae53dca791804847d78b31731615894e292948397c971c87ac9e95bd"
-url = "https://github.com/go-task/task/releases/download/v3.52.0/task_linux_amd64.tar.gz"
-url_api = "https://api.github.com/repos/go-task/task/releases/assets/464613330"
-
-[[tools.zig]]
-version = "0.16.0"
-backend = "core:zig"
-
-[tools.zig."platforms.linux-arm64"]
-checksum = "sha256:ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17"
-url = "https://ziglang.org/download/0.16.0/zig-aarch64-linux-0.16.0.tar.xz"
-
-[tools.zig."platforms.linux-x64"]
-checksum = "sha256:70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00"
-url = "https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz"
-provenance = "minisign"
diff --git a/mise.toml b/mise.toml
deleted file mode 100644
--- a/mise.toml
+++ /dev/null
@@ -1,38 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-min_version = "2026.7.5"
-
-[settings]
-experimental = true
-
-[tools]
-buf = "1.67.0"
-gh = "2.96.0"
-go = "1.26.6"
-node = "24.18.0"
-task = "3.52.0" # Removed after Taskfile.yml is migrated to mise tasks.
-zig = "0.16.0"
-
-[bootstrap.packages]
-# APT
-"apt:apt-utils" = "latest"
-"apt:createrepo-c" = "latest"
-"apt:debsigs" = "latest"
-"apt:gnupg" = "latest"
-"apt:openssl" = "latest"
-"apt:rpm" = "latest"
-"apt:zip" = "latest"
-
-# DNF
-"dnf:createrepo_c" = "latest"
-"dnf:gnupg2" = "latest"
-"dnf:openssl" = "latest"
-"dnf:rpm" = "latest"
-"dnf:zip" = "latest"
-
-[task_config]
-dir = "{{cwd}}"
-includes = [
- "git::https://github.com/dimidiumlabs/platform.git//tasks?ref=8bc35fe8be889c50db2d1fb4425cc1b4097dc6b8",
-]
diff --git a/nfpm.yaml b/nfpm.yaml
index a0fc6ad..3ade60c 100644
--- a/nfpm.yaml
+++ b/nfpm.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
# yaml-language-server: $schema=https://nfpm.goreleaser.com/schema.json
@@ -33,13 +33,6 @@ contents:
file_info:
mode: 0755
- # mirum-agent guest binaries — payload the worker injects into VMs.
- # Not host executables; the full guest matrix ships in every package.
- - src: build/agent/mirum-agent-*
- dst: /usr/lib/mirum/agent/
- file_info:
- mode: 0755
-
- src: packaging/server/config.yaml
dst: /etc/mirum/server/config.yaml
type: config|noreplace
@@ -127,6 +120,6 @@ rpm:
apk:
signature:
key_file: ${APK_SIGNING_KEY}
- # nFPM appends ".rsa.pub"; the versioned basename lets clients retain
- # historical package keys during rotation.
- key_name: packages.${PACKAGE_KEY_VERSION}
+ # nfpm appends ".rsa.pub" to this value when embedding the signature;
+ # clients must install the public key as /etc/apk/keys/mirum.rsa.pub.
+ key_name: mirum
diff --git a/packaging/apt-ftparchive.conf b/packaging/apt-ftparchive.conf
new file mode 100644
--- /dev/null
+++ b/packaging/apt-ftparchive.conf
@@ -0,0 +1,29 @@
+// Copyright (c) 2026 Nikolay Govorov
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+Dir {
+ ArchiveDir ".";
+ CacheDir "/tmp/apt-cache";
+};
+
+Default {
+ Packages::Compress ". gzip";
+ Packages::Extensions ".deb";
+};
+
+TreeDefault {
+ Packages "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages";
+ BinCacheDB "packages-$(DIST)-$(ARCH).db";
+};
+
+Tree "dists/stable" {
+ Sections "main";
+ Architectures "amd64 arm64 riscv64 ppc64le";
+ Directory "pool/stable";
+};
+
+Tree "dists/nightly" {
+ Sections "main";
+ Architectures "amd64 arm64 riscv64 ppc64le";
+ Directory "pool/nightly";
+};
diff --git a/packaging/dl/404.html b/packaging/dl/404.html
index a873a60..3ae5a33 100644
--- a/packaging/dl/404.html
+++ b/packaging/dl/404.html
@@ -1,5 +1,5 @@
<!--
- SPDX-FileCopyrightText: 2026 Nikolay Govorov
+ Copyright (c) 2026 Nikolay Govorov
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<!DOCTYPE html>
diff --git a/packaging/dl/index.html b/packaging/dl/index.html
index 981e3a0..60c970d 100644
--- a/packaging/dl/index.html
+++ b/packaging/dl/index.html
@@ -1,5 +1,5 @@
<!--
- SPDX-FileCopyrightText: 2026 Nikolay Govorov
+ Copyright (c) 2026 Nikolay Govorov
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<!DOCTYPE html>
diff --git a/packaging/mirum-server.initd b/packaging/mirum-server.initd
index b638451..3aa4d99 100644
--- a/packaging/mirum-server.initd
+++ b/packaging/mirum-server.initd
@@ -1,5 +1,5 @@
#!/sbin/openrc-run
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
description="Mirum daemon (modern CI platform)"
diff --git a/packaging/mirum-server.service b/packaging/mirum-server.service
index 2d3834b..69e440f 100644
--- a/packaging/mirum-server.service
+++ b/packaging/mirum-server.service
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
[Unit]
diff --git a/packaging/mirum-worker.initd b/packaging/mirum-worker.initd
index 5a1c457..dac0bf7 100644
--- a/packaging/mirum-worker.initd
+++ b/packaging/mirum-worker.initd
@@ -1,5 +1,5 @@
#!/sbin/openrc-run
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
# Templated service. To run a worker named "default":
diff --git a/packaging/mirum-worker@.service b/packaging/mirum-worker@.service
index ccbec8d..dbbc5e0 100644
--- a/packaging/mirum-worker@.service
+++ b/packaging/mirum-worker@.service
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
[Unit]
diff --git a/packaging/scripts/postinstall.sh b/packaging/scripts/postinstall.sh
index 1cc5379..3479c75 100644
--- a/packaging/scripts/postinstall.sh
+++ b/packaging/scripts/postinstall.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
set -e
diff --git a/packaging/scripts/preinstall.sh b/packaging/scripts/preinstall.sh
index a4793fa..160d0be 100644
--- a/packaging/scripts/preinstall.sh
+++ b/packaging/scripts/preinstall.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
set -e
diff --git a/packaging/scripts/preremove.sh b/packaging/scripts/preremove.sh
index 169954a..8d6c6ab 100644
--- a/packaging/scripts/preremove.sh
+++ b/packaging/scripts/preremove.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
set -e
diff --git a/packaging/server/config.yaml b/packaging/server/config.yaml
index 745b120..6ea9881 100644
--- a/packaging/server/config.yaml
+++ b/packaging/server/config.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
# Ignored when the corresponding systemd socket activation fd is present.
diff --git a/packaging/worker/default.yaml b/packaging/worker/default.yaml
index 4fa4de3..a0b8e39 100644
--- a/packaging/worker/default.yaml
+++ b/packaging/worker/default.yaml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# Copyright (c) 2026 Nikolay Govorov
# SPDX-License-Identifier: AGPL-3.0-or-later
server: localhost:2026
diff --git a/tools/licensegen/main.go b/tools/licensegen/main.go
index a175414..cafb861 100644
--- a/tools/licensegen/main.go
+++ b/tools/licensegen/main.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
// Command licensegen writes build/licenses.json — the third-party dependency
diff --git a/tools/licensegen/scan_go.go b/tools/licensegen/scan_go.go
index a560203..67b7e4b 100644
--- a/tools/licensegen/scan_go.go
+++ b/tools/licensegen/scan_go.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
diff --git a/tools/licensegen/scan_npm.go b/tools/licensegen/scan_npm.go
index 101dda7..0cb16f1 100644
--- a/tools/licensegen/scan_npm.go
+++ b/tools/licensegen/scan_npm.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
+// Copyright (c) 2026 Nikolay Govorov
// SPDX-License-Identifier: AGPL-3.0-or-later
package main