anonpenguin23 3676b000a6 chore: adopt DeBros DAO baseline rules + release 0.122.11
Standardization batch — no application code changes. Pulls in the
DeBros DAO baseline rules (v0.1.0, sha 51ce3f8) for supply-chain
defense and toolchain pinning.

Files added:
- DEBROS.md + debros.json — adopted-rules manifest
- .debros/compliance/{go,javascript-typescript,zig}.md — per-language
  compliance docs
- .github/workflows/security.yml — auto-detecting security CI
  (npm audit + go vulncheck), runs on main + weekly cron
- renovate.json — 30-day dependency cooldown, no auto-merge,
  vulnerability alerts bypass cooldown
- .nvmrc — pin Node 20.18.0
- vault/.zigversion — pin Zig 0.14.0
- sdk/.npmrc, website/.npmrc — supply-chain hardening
  (ignore-scripts, strict-peer-dependencies, save-exact, etc.)

Files modified:
- core/go.mod, os/agent/go.mod, website/invest-api/go.mod —
  add `toolchain go1.24.6` directive for reproducible builds
- VERSION + sdk/package.json — bump to 0.122.11
2026-05-12 11:10:10 +03:00

11 KiB

Compliance — Zig

The concrete files every Zig project must have to satisfy DEBROS.md.

Zig is the youngest ecosystem in this rules set. The good news: Zig's design avoids most supply-chain attack vectors (no install-time scripts, dependencies are content-addressed by hash). The bad news: there's no mature vulnerability database, no Renovate support, and no convention-defining popular packages to follow. Compliance leans heavily on manual review.

Status: Zig is pre-1.0 (current stable is 0.13.x as of late 2025). Build APIs change between releases. Treat this document as a moving target — verify the directives below still work on your project's pinned compiler.


Required files

1. build.zig.zon with explicit hashes for every dependency

Tier 3 block. Commits that add a dependency without an explicit hash are rejected.

Every dependency in build.zig.zon MUST include:

  • url — the source tarball
  • hash — the integrity hash Zig computes
.{
    .name = "your-project",
    .version = "0.1.0",
    .dependencies = .{
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.6.0.tar.gz",
            .hash = "1220abc123def456...",  // explicit, required
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

Zig's zig build will refuse to use a dependency whose downloaded content doesn't match the declared hash. This is equivalent to Go's go.sum and is the bedrock of Zig's supply-chain story.

Never use unhashed path = ... references to remote sources. Local path dependencies are fine for in-monorepo modules; remote sources must always be hashed.

2. .zigversion — pin the compiler

Convention file (read by zigup, mise, asdf via plugin):

0.13.0

CI MUST use the pinned compiler version, not "latest" or "master." Pre-1.0 Zig changes language semantics between minor versions; "latest" is not a safe default.

For projects on Zig master (development versions): commit the exact commit SHA, not "master."

3. Verify the compiler signature on install

The Zig compiler binary is signed with Andrew Kelley's minisign key, published at https://ziglang.org/download/. Every CI environment and every developer's machine MUST verify the signature when installing the compiler.

In CI:

- name: Install Zig with signature verification
  run: |
    ZIG_VERSION=$(cat .zigversion)
    curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz" -o zig.tar.xz
    curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz.minisig" -o zig.tar.xz.minisig
    minisign -Vm zig.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
    tar -xJf zig.tar.xz

The minisign public key above is the canonical one. Treat it as a pinned constant — if it changes, treat that change as a security event and verify out of band (mailing list, official site, multiple sources) before accepting.

4. Review every build.zig

Zig's build.zig is a Zig program. It runs at build time with full system access — it can read files, run subprocesses, hit the network. This is intentional (you can build C deps, run codegen, generate manifests) but it is also the equivalent of npm's postinstall problem at the build layer.

Rules:

  • The project's own build.zig MUST be reviewed line by line in PRs (it's not "configuration," it's executable code with full power)
  • Dependencies' build.zig files MUST be read when adding the dependency. Subprocess invocations (std.process.Child), file writes outside the cache, or network calls are red flags
  • No dependency may invoke std.process.Child to run shell scripts at build time without explicit allowlisting in debros.json.compliance.exceptions[] with a one-line justification

This is the single largest supply-chain risk in Zig. The compiler can't tell "legit codegen" from "exfiltrate ~/.ssh/." Human review is mandatory.

5. Lockfile-equivalent in CI

Zig doesn't have a separate lockfile; build.zig.zon's hash fields ARE the lockfile. CI MUST refuse to build if zig build would update build.zig.zon:

- name: Verify build.zig.zon is up to date
  run: |
    cp build.zig.zon build.zig.zon.expected
    zig build --fetch
    diff build.zig.zon build.zig.zon.expected

zig build --fetch resolves dependencies without compiling; if it would mutate build.zig.zon, the diff fails.

6. Compiler-version pinning in CI

Match the .zigversion:

- name: Install pinned Zig
  uses: mlugg/setup-zig@v1
  with:
    version-file: .zigversion

(mlugg/setup-zig is the community-maintained action with signature verification built in.)


File-by-file checklist

File Path Required? Tier-3 block?
build.zig.zon with hashes for every remote dep repo root
.zigversion repo root
CI workflow with compiler signature verification .github/workflows/security.yml (or equivalent)
CI step verifying build.zig.zon is up-to-date same

Code patterns to enforce

Error handling — Zig's error unions are the friend

Per DEBROS.md §2.2 principle 6: errors carry context. Zig's error types are great but easy to misuse:

// Good — explicit error set, useful context
pub const ConnectError = error{
    Timeout,
    ConnectionRefused,
    AddrInUse,
};

fn connectOlric(port: u16) ConnectError!Connection {
    return Connection.init(port) catch |err| switch (err) {
        error.Timeout => return error.Timeout,
        error.ConnectionRefused => {
            std.log.err("olric connection refused on port {d}", .{port});
            return error.ConnectionRefused;
        },
        else => return err,
    };
}

// Forbidden — silent swallow
fn connectOlric(port: u16) ?Connection {
    return Connection.init(port) catch null;  // hides why it failed
}

The try keyword bubbles errors; catch MUST handle them meaningfully (log + return, transform to a domain error, etc.) — never catch unreachable outside of provably-impossible cases.

Allocator discipline

Per DEBROS.md §2.2 principle 4 (validate at boundaries, trust internal code): every public function that allocates takes an std.mem.Allocator parameter. No global state, no hidden allocations.

// Good
pub fn parseConfig(allocator: Allocator, source: []const u8) !Config { ... }

// Forbidden
pub fn parseConfig(source: []const u8) !Config {
    const allocator = std.heap.page_allocator;  // hidden global
    ...
}

Tests use std.testing.allocator (catches leaks). Production uses a configured allocator (general-purpose arena, fixed buffer, etc.).

defer for cleanup; errdefer for error paths

Every allocation has a matching defer free (always cleanup) OR errdefer free (cleanup on error only, transfer ownership on success). Ad-hoc cleanup at the bottom of functions is forbidden.

File and function sizes

Per DEBROS.md §2.1:

  • Functions ≤50 lines
  • Files ≤300 lines

There's no widely-used Zig linter for this yet. Enforce via PR review checklist until tooling lands.

comptime discipline

comptime is powerful but easy to abuse. Rules:

  • Use comptime for type-level computation (generic containers, compile-time validation of constants)
  • Never use comptime for "performance" without measuring
  • comptime code is subject to the same length and complexity caps as runtime code
  • A comptime branch that grows past 30 lines is a code smell — extract to a named function

Testing

Zig's built-in test runner is the standard:

test "parseCron rejects empty input" {
    try std.testing.expectError(error.EmptyExpression, parseCron(""));
}
  • Tests live alongside source (test { ... } blocks in the same file, OR *_test.zig files)
  • Run via zig build test
  • CI MUST run tests on every PR
  • Unit suite total runtime <30s (DEBROS.md §2.4)
  • No std.time.sleep in tests — poll a readiness condition or use a fake clock

Dependency additions

When adding a Zig dependency, the agent MUST:

  1. Pin a tag, not a branch. refs/tags/v0.6.0 is OK; refs/heads/main is not. Branch refs are mutable; tags should be immutable (verify the tag isn't a moving target on the upstream — some repos rewrite tags).
  2. Read the dep's build.zig for subprocess invocations, network calls, or file writes outside the cache. Each is a red flag that requires justification.
  3. Verify the hash. After adding the dep, run zig build --fetch and confirm the computed hash matches what the upstream advertised.
  4. Check the maintainer's track record. Single-author, low-star Zig repos are higher risk simply because the language attracts experimental code. Prefer deps with an active community.
  5. Note the lack of Renovate support. Zig dep updates are manual. Document the upstream tag-tracking process in a comment in build.zig.zon.

Migration from a stock Zig project

  1. Pin the compiler. Add .zigversion.
  2. Audit build.zig.zon. Every remote dependency must have a hash. Run zig build --fetch and copy the computed hashes in.
  3. Read every build.zig in your dependency tree. Flag anything that runs subprocesses or hits the network at build time. Open issues upstream OR find alternatives.
  4. Add CI with compiler signature verification and zig build --fetch lockfile check.
  5. Update debros.json to record Zig compliance is satisfied. Note any build.zig exceptions you accepted in compliance.exceptions[].

Expect first migration to take a day for projects with several deps — the build.zig review is the slow part.


Notes on Zig's supply-chain story

What Zig protects against (by design):

  • Hash-pinned dependencies. build.zig.zon mutation is loud; a swapped dep fails to build.
  • No install-time scripts. Dependencies don't run code when fetched (unlike npm postinstall).
  • No package registry to compromise. Deps are URLs (usually GitHub tarballs); there's no central index to attack. Each upstream's compromise is isolated.
  • Cryptographically-signed compiler releases. The official ziglang.org binaries are minisigned.

What Zig does NOT protect against:

  • build.zig running arbitrary code at build time. This is the equivalent of npm postinstall, but always-on. Human review of every dep's build.zig is the only defense.
  • Compromised upstream repos. Hash-pinning catches changes to already-fetched versions, but a malicious new release still has whatever malicious content it ships with. There's no pip-audit / govulncheck equivalent yet.
  • Tag rewriting. Some upstreams rewrite tags. Hash-pinning catches this on re-fetch, but the social signal is missed. Prefer upstreams with a no-tag-rewrite policy.
  • Renovate support. None yet. Track dep updates manually. Open a Renovate config issue upstream if your CI infra needs auto-PRs.

Zig is the youngest ecosystem here and tooling is still catching up. Until the Zig package registry (or an equivalent) emerges, manual review is the floor.