mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-16 22:54:12 +00:00
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
253 lines
11 KiB
Markdown
253 lines
11 KiB
Markdown
# Compliance — Zig
|
|
|
|
> The concrete files every Zig project must have to satisfy [DEBROS.md](../../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
|
|
|
|
```zig
|
|
.{
|
|
.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:
|
|
|
|
```yaml
|
|
- 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`:
|
|
|
|
```yaml
|
|
- 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`:
|
|
|
|
```yaml
|
|
- 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:
|
|
|
|
```zig
|
|
// 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.
|
|
|
|
```zig
|
|
// 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:
|
|
|
|
```zig
|
|
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.
|