AlphaCOLLAB runs on GenesisL1 mainnet and has had no external audit. Use small amounts.Security notes

COLLAB

Testing

COLLAB is tested in four layers: the Foundry suite for the contracts, end-to-end suites that drive the real site in a browser against a local chain, the server kit's unit tests and sandbox install, and static checks of the web app and of every generated file. This page says what each layer covers, how to run it and how to add to it. It is for developers and reviewers; the tests are part of every release build (Building a release).

Part
Developers
Version
COLLAB 2.2.0
Updated
Reading time
12 min

At a glance

LayerCommandSize in 2.2.0
Contractsforge test331 tests in 92 suites
Deeper invariantsFOUNDRY_PROFILE=ci forge test --match-path "test/invariants/*"25 tests in 7 suites
End to endpython3 tools/e2e/run.py [filter]161 scenarios in 17 suites
Visual sweeppython3 tools/e2e/sweep.py --base <web> --rpc <rpc>the main pages and every DAO tab, light, dark and phone
Server kit, unitpython3 -m unittest discover -s server/tests32 tests
Server kit, installsudo server/tests/sandbox_install.sh [--keep]install, re-run, upgrade, rollback
Web apppython3 tools/lint_web.pyevery script and page

For release 2.2.0 every layer passed: the Foundry suite in the release build (331 of 331), the end-to-end suites on the extracted archive, the 32 unit tests and the sandbox install. Passing tests are not an audit: COLLAB 2.2 has had AI-assisted internal reviews and no external audit.

The Foundry suite

forge build --skip test --skip script     # the contracts, then a few test files per build (memory)
forge test                                # 331 tests in 92 suites
forge test --match-path test/Profile.t.sol -vv
FOUNDRY_PROFILE=ci forge test --match-path "test/invariants/*"
FolderSuitesTestsWhat it holds
test/*.t.sol29167unit and integration tests on the full stack
test/invariants/725invariant campaigns
test/audit/56139the reviews' proof-of-concept tests and fuzz checks

test/Base.t.sol deploys the whole protocol with the real CIPNFT (twice), GL1F and GL1F Crypto bytecode from externals/bin/, so every proposal type runs end to end against the contracts DAOs use on GenesisL1. The main files:

FileTestsCovers
Governance.t.sol36the proposal lifecycle, deposits and penalties, quorum, veto, threshold and abstain cases, delegation, snapshots
Profile.t.sol30the 2.2 profile: PNG checks, 128 × 128, 512 KB, rewrites in place, chunk reads (fuzzed), the description's text rules and 256 KB, slugs (fuzzed against a reference), the directory, and ProfileGasTest
Integrations.t.sol22each DAO's integrations: lists, roles, rule precedence (fuzzed), listing within one proposal, removal, misbehaving cross-reference getters
Modules.t.sol16constitution versions, representatives and the permanent title, operator calls through module permissions, the treasury's asset kinds, ERC-1271 signatures, locked implementations, airdrops, TextStore's large blobs, the lens summaries
GovernorHardening.t.sol14the outside review's governance items (OR-2 to OR-4) and the 2.1 review's: the minimum Yes, critical rules never laxer than standard ones, freeze votes side by side
Utf8.t.sol12the text checks against a reference decoder written differently
DeSciFi.t.sol, Launcher.t.sol, GL1F.t.sol, CipherVault.t.sol, CipherVaultMulti.t.sol, HoldingVotes.t.sol34pools and fees, the issuance example from the specification, a DAO earning GL1F inference fees, key custody, purchase, reveal and sale on CIPNFT, one key across several CIPNFT contracts, votes with a holding period
Invariants.t.sol2two quick invariants: share units and votes conserved, pool fees always covered
SizeLimits.t.sol1every COLLAB contract fits EIP-170's 24,576 bytes (local chains run anvil with a raised limit, so this test is what catches a contract GenesisL1 would refuse)

Fuzz tests run 512 cases each, 4,096 with FOUNDRY_PROFILE=ci. Invariant campaigns run 64 runs of depth 48, 128 of depth 96 under ci; four suites (DistributorInvariants, GovernorInvariants, LauncherInvariants and TreasuryStanceInvariants) set their own in the file (/// forge-config: lines). fail_on_revert is off: the handlers predict from the rules whether each call must succeed, so a call that should have failed counts as a violation, and so does one that should have worked.

Invariant suiteWhat always holds
ShareTokenInvariantsvoting units equal the balances of eligible accounts and add up to the total; each delegate holds exactly its delegators' units, now and at every past block; lock claims are counted exactly; only the DAO mints and burns
DAOTokenInvariantsthe supply never exceeds the cap; powers given up never return; a pause blocks transfers between holders; only the owner administers
LauncherInvariantsallocations add up to the supply and reach their destinations; pools hold exactly what was asked; the launcher keeps nothing
DistributorInvariantseach claim pays exactly what the snapshot entitles, once; the contract holds what open airdrops owe
DeSciFiInvariantsswaps never lower the constant product of the effective reserves; deposits and withdrawals never dilute others; fees owed never exceed fees held; lock boxes release only to their owner after the date
GovernorInvariantsno vote weighs more than its voter held or was lent at both readings; frozen accounts never vote, propose or delegate
TreasuryStanceInvariantsin a DAO where only treasury spends are critical, no action the policy calls standard leaves the treasury with less, across two GL1F collections and two CIPNFT contracts

The review suites

test/audit/ keeps the proof-of-concept tests of the internal reviews, each now asserting the fixed behaviour: AuditPoC.t.sol (release 1.0's review), with the reviewers' regression and fuzz checks in Controls, Informational, LaunchPoolFuzz, LockClaims and VoteFuzz; TokenReviewV2.t.sol and the three TokenVerify*.t.sol files (2.0's token review and its two rounds of verification); Review21.t.sol and Verify21.t.sol (2.1's review and its verification); and Hardening22.t.sol (H22-1 and H22-2, found while building 2.2). Security maps each finding to its test. The 2.2 contract review ran its proof-of-concept tests (RV22-1 to RV22-8) in the reviewer's own copy; its findings are documented in Security or handled in the interface, and they have no test in this suite.

End-to-end tests

tools/e2e/run.py drives headless Chromium through Playwright against the real contracts on a local chain, with an injected test wallet. It starts its own sandbox from the snapshot (see Local development), resets the chain before every scenario, and stops the sandbox at the end.

python3 tools/e2e/run.py                               # every scenario
python3 tools/e2e/run.py profile                       # scenarios whose "<module>.<function>" contains "profile"
python3 tools/e2e/run.py descifi --name mine --rpc-port 8601 --web-port 8701 --keep

--name, --rpc-port and --web-port pick the sandbox (default e2e, 8690, 8790), and --keep leaves it running. Each scenario prints PASS or FAIL with its time; a failure saves a screenshot of every open browser (build/screenshots/FAIL-<scenario>-<n>.png) and prints the console errors, and the run exits 1. All scenarios share one Chromium process. On a machine shared with other people's runs, run browser suites one at a time, for example under flock with a lock file of your own.

SuiteScenariosCovers
admin5sign-in, deploy and publish, publishing a record and site settings, a host without the service, the page's checks matching the server's
create7the wizard: limits matching the contracts, minimal and full creations, validation and drafts, a narrow screen, GL1F Crypto only, a malformed title
deploy7the Deploy and Check tabs: a deployment, a reload while sending, without a site configuration, checking configurations and tampered records, the names rule, config.js byte for byte equal to the tool's
descifi18swaps (native L1, launch pools, several hops), liquidity, pools, locks, fees, wrapping, requotes, unknown and look-alike tokens, without a wallet
docs10the docs home and index, a page's parts and anchors, search, the questions page's structured data, figures, the live figures (drawings and this site's contracts), a dark phone, widths from 320 to 2560 px, the 404 page, the manifest, icons and crawler files
governance_builders18the proposal builders: sends, rules, constitution, representatives, freezes, signatures, airdrops, NFTs, policy rules, integrations, raw calls
governance_core13a raw transaction's lifecycle, deposits, delegation overrides, the workspace and proposal pages, the composer, critical escalation, the minimum Yes, freeze votes, refresh races, phones
landing9the network molecule against the chain, the largest DAOs and votes, links, the directory's URL state and empty states
market7/tokens and token pages against tokens.json, without server data, hostile names, right after a deployment, lopsided pools, vouching
models7GL1F: paid inference after a pricing vote, donations, sends and deletes, listings, registration per collection, GL1F Crypto's rules
portal10the shell, the phone drawer, search, /votes, site settings, the site's price, the undeployed mode, the cookie notice, the settings bar, capped reads
profile8slug URLs, a logo by proposal, a heavy logo and creation on a node capped at 25M, the description limit, the permanent title, hostile and copycat titles
review3spoofed token symbols, cleaned proposal titles
sealed4key epochs, purchase, reveal, mint, sale, declassification, stewards and key rotation, a second CIPNFT contract
seo13the prerender: the glyph port, the demo DAOs, robots.txt on another main host, hostile and malformed DAOs, Markdown text, the member network, server configurations and the sitemap against the pages, config.js origins, static hosts, a hidden DAO, the /pools and /locks heads, failing parts
tokens7issuance, an airdrop claim, minting, liquidity and locks, critical value actions, every builder submitting cleanly
views15treasury values and flows, sweeps, lock fees, airdrops, NFTs per integration, members, settings (rules, pending changes, the treasury stance, outside keys), representatives, constitution versions

The sealed suite needs the reference CIPNFT dapp (CIPNFT_REF). The admin suite runs the real admin service behind tools/e2e/admin_site.py, a local HTTPS front that does what nginx does. The suites that need server data make it with the real tools (tools/seo/tokens.py, tools/prerender.py) and serve it with tools/serve_local.py --data or --prerender.

The harness

tools/e2e/harness.py holds Browser(base, rpc, account, width=1360, height=900, dark=False, cookie_notice=False, ignore_https_errors=False): a browser context with the test wallet for account (anvil's unlocked development accounts), the cookie notice marked as seen unless cookie_notice=True, and every console error and warning collected in errors. Its helpers:

HelperDoes
go(path, wait=1500)loads a page by its clean path
nav(path)follows an in-app link without a page load, so what the tab holds in memory stays
wait_text(text), click(name, role="button"), fill(label, value)waits, clicks and types by visible text, role and label
wait_toast(text="Confirmed")waits for a transaction toast
assert_no_errors()fails on any console error or page error
shot(name)a full-page screenshot in build/screenshots/
pagethe Playwright page, for everything else

tools/e2e/sweep.py opens the home page, /daos, /create, /swap, /pools, /pools/add, /locks, /docs, /settings, /privacy, /admin and a 404, then every tab of one DAO (--dao, default 1), in light, dark and a 390 px phone layout. It saves screenshots to build/screenshots/sweep/ and exits non-zero when a page logs an error or scrolls sideways on a phone. /votes, /tokens and the pages under /docs/ are not in its list: their own suites (portal, market, docs) test them.

Writing a scenario

A suite is a module in tools/e2e/scenarios/ that exports SCENARIOS, a list of functions. Each function receives an Env and fails by raising, with an assert or any exception; the runner gives it a freshly reset chain and closes its browsers afterwards.

# MIT License. Copyright (c) 2026 Decentralized Science Labs LLC.
"""Example: a DAO opened by its treasury address lands on its slug URL."""


def address_url_moves_to_slug(env):
    b = env.browser(0)                         # account #0, with the test wallet
    b.go(env.dao_url(1))                       # /dao/<treasury of DAO 1>
    b.wait_text("Open Proteomics Collective")
    assert b.page.url.endswith("/dao/open-proteomics-collective"), b.page.url
    b.assert_no_errors()


SCENARIOS = [address_url_moves_to_slug]
Env memberGives
browser(i, **kw)a Browser for account i (None for no wallet), with the harness's options
dao(n)DAO n's addresses: treasury, governor, shares, profile, policy, vault, creator, createdAt, createdBlock
dao_url(n, *parts, preset=)the clean path of DAO n or one of its sections, with a composer preset
send(i, to, sig, *args, value="0"), cast(*args)a transaction from account i with cast send --unlocked; any cast command against the sandbox
warp(seconds), mine(n), rpc(method, params)the chain clock, blocks and raw JSON-RPC
accounts, deploy, demo, web, rpc_urlthe twelve accounts, deployments/local.json, deployments/demo.json, the sandbox's web and RPC URLs

Check what you build by looking at it: take screenshots, in light and dark and at 390 px, and read them. Keep scenarios independent of each other's state, prefer visible text and roles to CSS selectors, and wait for what the user waits for (a toast, a text) rather than for fixed times.

Server tests

python3 -m unittest discover -s server/tests        # 32 tests, a few seconds
sudo server/tests/sandbox_install.sh                # the installer end to end, in a sandbox

server/tests/test_admin.py tests the admin service and its helpers without a server: the site settings and record checks of tools/write_web_config.py, config.js and its policy, keccak-256 vectors, sessions (durable sign-out, damaged session files), the sign-in limit, safe reads and writes that never follow a link, the nginx scan, the API's routes, proxy secret, cross-site checks, bodies and flow, the history's bound, and the prerender and policy jobs. The policy-step test needs root. server/tests/adminenv.py writes the throwaway settings these tests and tools/e2e/admin_site.py use.

server/tests/sandbox_install.sh runs as root and needs nginx, python3, openssl and curl. It builds a private nginx (its own configuration, PID file and high ports: 8088, 8443 and 8417 by default, or the next free ones, --http-port, --https-port, --admin-port) with another site on it, in a folder under /var/tmp that it removes unless --keep. It checks that the installer refuses while that site claims one of COLLAB's names, installs with --prefix, --etc, --self-signed and --no-systemd, runs nginx -t, and then runs server/tests/sandbox_checks.py against the site: redirects, headers, caching, the 204s, the prerender overlay's limits and planted links, config.js, the admin API (cross-site checks, schemas, publish and unpublish, the prerender job, sign-out and revocation), the policy step with analytics, deploy/check_site.py, a re-run that changes nothing, an upgrade to a newer release and the rollback, the rate limits, the Let's Encrypt path with stubbed certbot and DNS (stub_certbot.sh, stub_getent.sh, stub_check_certs.py), and the refusal of a symbolic link planted in shared/. It stops only what it started, by process id, and leaves the system users collab and collab-prerender on the machine.

Lint and generated files

python3 tools/lint_web.py checks the rules of The web app that a script can check, and must report 0 errors: raw-HTML sinks and dynamic code in scripts, remote resources, hand-written page links, inline scripts, styles and handlers, a missing or differing Content-Security-Policy, relative paths, canonical links, missing imports, scripts, stylesheets and icons, the licence line of every script, page and stylesheet in web/ and every Python file in tools/, stale shells, stale /pools and /locks copies, and the documentation as tools/build_docs.py --check reports it.

Generated files have checks of their own, each exiting 1 when its file is stale:

CheckVerifies
python3 tools/shell_html.py --checkthe portal shell and preload blocks of every entry
python3 tools/prerender.py --static-pages --checkweb/pools/ and web/locks/
python3 tools/build_docs.py --check [--strict]the docs sources, the generated docs pages and the docs pages web/sitemap.xml lists; --strict also fails placeholder pages
python3 tools/docs_figures.py --checkevery figure the docs pages show
python3 tools/make_assets.py --check, python3 tools/make_demo_logos.py --checkthe app icons and the demo DAOs' logos
python3 tools/export_deploy_artifacts.py --checkthe deploy bundle against the compiled contracts
python3 tools/merge_css.py --checkthat no CSS part is still imported
python3 deploy/sync_csp.py --checkthe nginx and Caddy configurations and web/_headers against the pages' policy
python3 tools/version_assets.py web --checkin a release tree: every asset URL carries the release's ?v=

The docs renderer has unit tests of its own, which the release build runs: python3 tools/tests/test_build_docs.py (escaping, links, headings, tables, callouts, code, lists, figures, the FAQ's structured data and the checks --check makes).