aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolay Govorov <me@govorov.online>2026-05-17 03:04:58 +0100
committerNikolay Govorov <me@govorov.online>2026-05-17 04:13:14 +0100
commit10a73f38116adb4003bdf4c4e180f3bd5b9177a3 (patch)
tree0f27fb70f07b5c418593a760eaf7346ae5932d04
parent83cf86e65529d897d16f134a20618e1685415a0a (diff)
downloadtar
tar.gz
tar.bz2
tar.lz
tar.xz
tar.zst
zip
Bootstrap mirum agent
Diffstat
-rw-r--r--.github/workflows/build.yml5+5 −0
-rw-r--r--.gitignore5+5 −0
-rw-r--r--REUSE.toml1+1 −0
-rw-r--r--Taskfile.yml60+60 −0
-rw-r--r--cmd/mirum-agent/build.zig113+113 −0
-rw-r--r--cmd/mirum-agent/build.zig.zon17+17 −0
-rw-r--r--cmd/mirum-agent/main.c10+10 −0
-rw-r--r--cmd/mirum-agent/mirum-agent.c10+10 −0
-rw-r--r--cmd/mirum-agent/mirum-agent.h9+9 −0
-rw-r--r--cmd/mirum-agent/test.c22+22 −0
-rw-r--r--nfpm.yaml7+7 −0
11 files changed, 259 insertions, 0 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a8310bb..38bb2dc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -29,6 +29,11 @@ jobs:
with:
go-version-file: go.mod
+ - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
+ with:
+ mirror: https://pkg.earth/zig # maintained by mirum author
+ version: 0.16.0 # keep in sync with build.zig.zon
+
- name: Install tools
run: |
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list
diff --git a/.gitignore b/.gitignore
index e9e681e..426c177 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,11 @@
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/REUSE.toml b/REUSE.toml
index 791cdce..5a6cb57 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -8,6 +8,7 @@ path = [
".mailmap",
"CLA.md",
"VERSION",
+ "cmd/mirum-agent/build.zig.zon",
"cmd/mirum-server/web/*.json",
"go.mod",
"go.sum",
diff --git a/Taskfile.yml b/Taskfile.yml
index 4ba97ed..6dcac22 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -16,6 +16,15 @@ 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
@@ -114,8 +123,55 @@ 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: [
@@ -140,8 +196,10 @@ 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"
if [ "$os" = "windows" ]; then
(cd "$staging" && zip -qr "$OLDPWD/{{.DIST_DIR}}/mirum-${target}.zip" .)
@@ -198,6 +256,7 @@ tasks:
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
ARCH=$arch nfpm package --packager deb --target {{.DIST_DIR}}/
@@ -515,5 +574,6 @@ 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/cmd/mirum-agent/build.zig b/cmd/mirum-agent/build.zig
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/build.zig
@@ -0,0 +1,113 @@
+// Copyright (c) 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
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .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
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/main.c
@@ -0,0 +1,10 @@
+// Copyright (c) 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
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/mirum-agent.c
@@ -0,0 +1,10 @@
+// Copyright (c) 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
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/mirum-agent.h
@@ -0,0 +1,9 @@
+// Copyright (c) 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
new file mode 100644
--- /dev/null
+++ b/cmd/mirum-agent/test.c
@@ -0,0 +1,22 @@
+// Copyright (c) 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/nfpm.yaml b/nfpm.yaml
index 3ade60c..2fa1f6b 100644
--- a/nfpm.yaml
+++ b/nfpm.yaml
@@ -33,6 +33,13 @@ 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