Installing COLLAB on a server
server/install.sh sets up a complete COLLAB site on a Debian or Ubuntu server with nginx. This is
the complete reference for it: what to prepare, the commands, every option, what the installer
prints, and the files, users and services it leaves behind. It sets up:
- HTTPS. A Let's Encrypt certificate for every name, renewed by itself.
- The names. The site at https://collab.genesisl1.com. dao.genesisl1.com,
collab.descifi.org and dao.descifi.org answer
301to the same path and query there. - The admin page at
/admin: deploy the contracts from your wallet, publish them on this site, change the site's settings, watch its status. - The prerender job. It gives every DAO and token a page of its own for search engines and link previews, and keeps the logos, descriptions, token list and L1 price cached.
The site itself stays serverless. Visitors' browsers read the chain directly, and every page keeps working when the admin service or the prerender job is stopped. COLLAB 2.2 is an alpha: it has had no external audit (Security). Running a COLLAB site explains what the server does and does not do.
What you need
- A server with Debian 11 or later, or Ubuntu 22.04 or later, root access, and ports 80 and 443 reachable from the internet.
- The release archive,
collab-2.2.0.tar.gz, and its.sha256file. - For deploying the contracts: a browser wallet with some L1 for gas (section 4).
Before you install is the checklist: server, ports, DNS, firewall, an nginx that serves other sites, the main host and the RPC.
The installer adds what is missing: nginx, certbot, python3 with Pillow and pycryptodome, openssl and curl.
- nginx may already serve other sites. The installer adds one file of its own and never touches theirs, nor the default site.
- It refuses to go on when another site already answers for one of COLLAB's names (section 9).
1. DNS
Point each name at the server before you install: an A record with its IPv4 address and, when the
server has IPv6, an AAAA record with that address.
| Name | Record | Role |
|---|---|---|
collab.genesisl1.com | A (and AAAA) | the site |
dao.genesisl1.com | A (and AAAA) | 301 to the site |
collab.descifi.org | A (and AAAA) | 301 to the site |
dao.descifi.org | A (and AAAA) | 301 to the site |
Check from your computer with dig +short collab.genesisl1.com, or from the server with
getent ahosts collab.genesisl1.com, which is what the installer reads. If the names are on Cloudflare, set them to DNS only (grey
cloud), at least until the certificate is issued.
The installer checks each name before asking for the certificate:
- a name passes when its DNS points at an address of this server;
- behind NAT or a proxy, it passes when a request through the name reaches this server.
A name that fails is left out of the certificate, with a warning. Run the installer again once its DNS is right. Before you install has the details.
2. Upload and install
From your computer, then on the server:
ssh root@SERVER mkdir -p /var/www/html # a new server has no such folder before a web server
scp collab-2.2.0.tar.gz collab-2.2.0.tar.gz.sha256 root@SERVER:/var/www/html/
ssh root@SERVER
cd /var/www/html
sha256sum -c collab-2.2.0.tar.gz.sha256
tar xzf collab-2.2.0.tar.gz
sudo ./collab-2.2.0/server/install.sh --email you@example.org--email is the contact address of the Let's Encrypt account; you may leave it out.
The extracted folder is only the installer's source:
The installer copies what the site needs to
/var/www/collab/releases/2.2.0, and nothing depends on the folder afterwards./var/www/htmlis the root of nginx's default site, so its files can be fetched underhttp://SERVER/collab-2.2.0/while it is there. It holds nothing secret.Delete the folder and the archive once the install is done, or keep them until the next upgrade:
sudo rm -rf /var/www/html/collab-2.2.0 /var/www/html/collab-2.2.0.tar.gz /var/www/html/collab-2.2.0.tar.gz.sha256
The installer prints what it does, step by step:
- Packages: installs only what is missing.
- Checking nginx: checks before changing anything, so a refused run leaves the server as it was.
- nginx's configuration must pass its own test (
nginx -T). - No other site may answer for COLLAB's names (wildcard names such as
*.genesisl1.comare reported but not refused: nginx prefers COLLAB's exact names). - No program other than nginx may hold ports 80 or 443.
- nginx's configuration must pass its own test (
- Users and folders: creates the system users
collab(the admin service) andcollab-prerender(the prerender job) and the folders of section 7. The folders undershared/are created so that a symbolic link or a folder of the wrong owner there is refused, never followed. - Release: copies the release to
/var/www/collab/releases/<version>, owned by root. Nothing is served from it yet. - Settings:
/etc/collab/collab.env, written on the first run with a new admin token and two secrets, and kept on every later run. Options you pass change only the values they name. - Site configuration:
config.jsfrom the site settings and the published deployment, written as usercollab. The content policy step writes the policyconfig.jsneeds into the new release's pages, and the helper/usr/local/sbin/collabis written. Until a deployment is published, every page says COLLAB is not deployed here yet. - nginx: COLLAB's own file,
/etc/nginx/sites-available/collab.conf, linked assites-enabled/zz-collab.conf. Every change is checked withnginx -t; when nginx refuses it, the previous file comes back and nginx keeps running as before. Only then does/var/www/collab/currentswitch to the new release. When the release or its assets version changed, the old prerendered pages are removed first; releases older than the two before are deleted. Until there is a certificate, port 80 answers Let's Encrypt's challenges and a short "being set up" notice. Which site answers names nobody configured is explained in Domains, redirects and certificates. - DNS and certificate: the check of section 1, then
certbot certonly --webrootfor one certificate namedcollabwith every name that passed. - nginx: HTTPS:
- the site on 443 with HTTP/2 (before nginx 1.25.1, only when the other sites on port 443 use it already), HSTS, the security headers and caching;
- one server that answers the other names with
301; - port 80 answers
301to HTTPS on the main name for every name, and keeps the challenge path open for renewals.
- Services: the systemd units of section 7, enabled and started, and the admin service
restarted. certbot's own timer renews the certificate, and the deploy hook the installer gives
certbot,
collab renewed, reloads nginx. Once HTTPS is on, the content policy step runs again (section 6). A prerender run is requested, and when ufw is active, ports 80 and 443 are allowed. - Checking: the site and the admin service are asked through nginx, on the server itself.
It ends with a summary. This one is from the test sandbox (server/tests/sandbox_install.sh),
which installs under other paths, on port 8443 and with a self-signed certificate; the admin token
is masked here:
COLLAB 2.2.0 is installed. It is an alpha: the site says so at the top of every page.
Site: https://collab.genesisl1.com:8443/
Redirected: dao.genesisl1.com, collab.descifi.org, dao.descifi.org (301 to the site, path and query kept)
Admin page: https://collab.genesisl1.com:8443/admin
Admin token: ******************************** (any time: sudo /var/tmp/collab-sandbox.RNfwGK/root/bin/collab admin-token)
Certificate: collab.genesisl1.com dao.genesisl1.com collab.descifi.org dao.descifi.org (self-signed, for testing)
Settings: /var/tmp/collab-sandbox.RNfwGK/etc/collab/collab.env (the token and secrets; root:collab 0640)
Files: /var/tmp/collab-sandbox.RNfwGK/root/current -> releases/2.2.0; /var/tmp/collab-sandbox.RNfwGK/root/shared (config.js, site.json, deployments, prerender, data)
Status, logs: sudo /var/tmp/collab-sandbox.RNfwGK/root/bin/collab status | logs
Next: open the admin page, sign in with the token, deploy the contracts from your wallet (Deploy),
then Publish to this site. Until then every page says COLLAB is not deployed here yet.
The folder this ran from (/var/tmp/collab-sandbox.RNfwGK/src/collab-2.2.0) and its archive are no longer needed: delete them, or keep
them until the next upgrade (run the new release's server/install.sh the same way).On a real server the helper is sudo collab admin-token, and the paths are /etc/collab and
/var/www/collab. With Let's Encrypt the certificate line ends "(Let's Encrypt, renewed by
certbot's timer)". Other lines that may appear:
Not covered: dao.descifi.org: point their DNS at this server, then run this again (the same command).COLLAB 2.2.0 is installed, but not on HTTPS yet: see the warnings above.when the main name has no certificate yet.This site is deployed: config.js names its contracts and was kept.in place of "Next: …" when a deployment is already published (an upgrade, or a second run).This ran from an installed release (/var/www/collab/releases/2.2.0): keep that folder, it is part of the site.in place of the last two lines, when you ran the installer from an installed release (section 8).
Options
| Option | What it does |
|---|---|
--main HOST | the site's address (default collab.genesisl1.com) |
--alias HOST | another name that answers 301 to it; repeat it for each name. The list replaces the default one, which is the other three of the four names above |
--no-aliases | no other names |
--email ADDRESS | contact address of the Let's Encrypt account (optional) |
--rpc URL | the RPC endpoint the prerender job reads (default: the site's own RPCs, in order); --rpc "" goes back to them |
--admin-port N | the admin service's port on 127.0.0.1 (default 8411) |
-h, --help | the installer's help text |
These are for testing only (server/tests/sandbox_install.sh uses them):
| Option | What it does |
|---|---|
--self-signed | a self-signed certificate instead of Let's Encrypt; browsers warn every visitor |
--prefix DIR | install under DIR instead of /var/www/collab, the helper as DIR/bin/collab |
--etc DIR | what goes to /etc goes to DIR instead: DIR/collab, DIR/nginx (its nginx.conf is the one tested and reloaded), DIR/systemd/system |
--no-systemd | start no services: the units are written, and the commands to run are printed |
--http-port N | HTTP on N instead of 80 |
--https-port N | HTTPS on N instead of 443 |
To make dao.genesisl1.com the main name on a first install, run the same command with
--main dao.genesisl1.com. The aliases become the other three, and every redirect, canonical link
and certificate follows.
Changing options later
The installer remembers --main, the aliases, --email, --rpc and the ports in
/etc/collab/collab.env. A later run without them keeps them; an option you pass changes only
what it names. --self-signed, --prefix, --etc and --no-systemd are not remembered: a run
without --self-signed asks Let's Encrypt for a certificate.
Run the same installer again with the option, from the extracted folder or from the installed copy:
sudo /var/www/collab/current/server/install.sh --rpc https://rpc.example.orgRun from an installed release, the summary ends "This ran from an installed release (…): keep that folder, it is part of the site."
Two options need care:
--aliasreplaces the whole list of aliases. Name every alias you want to keep.- Switching the main host after the install keeps the remembered aliases, so the old main name is
dropped unless you name it with
--alias. See Domains, redirects and certificates.
3. The admin page
Open https://collab.genesisl1.com/admin and sign in with the admin token.
- The installer prints the token;
sudo collab admin-tokenshows it again. sudo collab admin-token --newreplaces it and ends every session signed in with the old one.
Sessions last 12 hours. Signing out ends the session at once, and stays effective when the admin service restarts. After 10 failed sign-ins in 10 minutes from one address, sign-in waits until the first of them is 10 minutes old.
The tabs:
- Status: the release, the published deployment, the prerender job (last run, errors, warnings,
its log), the certificate, the content policy, the server and
config.js. - Deploy: deploys the protocol's contracts from your wallet. Any EIP-1193 browser wallet works (MetaMask, Rabby, Keplr in EVM mode): the page never asks for a key.
- Publish: puts a deployment on this site, or takes it off.
- Check: compares any deployment with this release, contract by contract.
- Site settings: the alpha strip, an announcement, featured and hidden DAOs, verified and hidden tokens, search-engine texts, links, Google Analytics, RPC endpoints and the explorer.
Deploy and Check work without signing in. /deploy answers 301 to /admin/deploy.
The admin page describes every tab.
4. Order of the first day
- Install (section 2) and open the admin page.
- Deploy the contracts on GenesisL1 from the Deploy tab: one transaction per contract, from your wallet. Let each transaction confirm before you reload the page. See Deploy the protocol from /admin.
- Publish to this site. The record is checked strictly:
- chain 29, a COLLAB DAO v2.2 version;
- every contract address present, non-zero and distinct, with a right EIP-55 checksum;
- the integration lists checked as
tools/write_web_config.pychecks them.
- Wait for the first prerender (Status; a few seconds to minutes), then open the site, create a small test DAO and run one proposal through.
- Optionally: site settings, Google Analytics, and
python3 /var/www/collab/current/deploy/check_site.py https://collab.genesisl1.com(Status, logs and the collab command).
To deploy with Foundry instead (Deploying COLLAB: reference, section 1), publish the record it writes from the Publish tab.
5. The deployment record and config.js
The server keeps one deployment for its chain (29), in /var/www/collab/shared/deployments/29.json.
- Publishing another record replaces it.
- Unpublish on the Publish tab (the admin API's
{"record": null}) takes the site back to its undeployed mode. - The replaced file is kept in
shared/history/, as every replacedconfig.jsandsite.jsonis (the newest 50 of each).
config.js is written only by tools/write_web_config.py: the admin service calls it, and so does
the installer. It holds:
- the record's addresses and integration lists;
"deployed";- the site settings as
"site"; - the RPC endpoints, explorer and analytics ID from the site settings;
- the site's address.
The page caches never keep it: a change takes effect on the next page load. Publish, unpublish and check covers the operator's side, and Data formats the file itself.
6. Site settings and the content policy
Site settings are kept in shared/site.json and checked strictly. Unknown keys, control or
invisible characters, URLs other than https, malformed slugs or addresses, and lists that are too
long are all refused, naming the field. Site settings describes every field.
Hiding changes nothing on chain or on other copies of COLLAB.
Some settings change the site's Content-Security-Policy: an analytics ID, or other RPC endpoints.
- The policy is the
<meta>tag of every page, and the same policy, withframe-ancestors, is nginx's header. - The admin service cannot change either, by design.
- It drops a request file instead.
collab-apply.paththen startscollab-apply.serviceas root.
That service:
- checks every value of
config.jsagain, and trusts nothing the admin service wrote; - builds the policy from origins only, and refuses one with any character it never needs;
- writes the policy into the release's pages and into COLLAB's own nginx file, and touches nothing else;
- checks nginx with
nginx -tand reloads it. When nginx refuses, it puts every file back and says why in Status (and insudo collab logs); - does nothing when the policy is unchanged;
- asks for a prerender run afterwards, so the prerendered pages carry the new policy too.
The installer runs the same step after every upgrade, so a new release gets the policy too.
sudo collab apply runs it by hand.
7. Files, users and services
/var/www/collab/
releases/<version>/ web/, tools/, server/, deploy/, docs/ of a release (root; only the policy step changes its pages)
current -> releases/<version>
shared/ collab, 0755
config.js the site's configuration (served at /config.js)
site.json the site settings
deployments/29.json the published deployment record
history/ the replaced versions (0750)
status/ the content policy step, the certificate's status and the sign-in sessions (0750)
jobs/ the prerender job's requests, status, log, lock and caches (collab-prerender, group collab, 2770)
prerender/ the prerender job's pages, preview images and sitemap (collab-prerender)
data/ its cache, served at /data/: logos, descriptions, tokens.json, price.json (collab-prerender)
acme/ Let's Encrypt's webroot
/etc/collab/ 0751
collab.env settings, the admin token and two secrets (root:collab 0640)
prerender.env the same settings without the secrets, for the prerender job (0644)
nginx-proxy.conf the secret header nginx adds for the admin service (root 0600)
tls/ only with --self-signed: the test certificate
/etc/nginx/sites-available/collab.conf, sites-enabled/zz-collab.conf
/etc/letsencrypt/live/collab/ the certificate (certbot's)
/usr/local/sbin/collab the helper| Unit | What it does |
|---|---|
collab-admin.service | the admin API, user collab, on 127.0.0.1:8411 |
collab-prerender.service | one prerender run, user collab-prerender |
collab-prerender.timer | the prerender every 10 minutes |
collab-prerender.path | a prerender as soon as the admin page asks for one |
collab-apply.path + collab-apply.service | the content policy step, as root, on request |
certbot.timer | certificate renewal (certbot's own) |
How the services are confined:
- The admin service runs sandboxed by systemd: no privileges, a read-only system, no network
beyond 127.0.0.1, and write access to
shared/only.- It accepts only requests that carry the secret header nginx adds.
- Every state-changing request must be JSON, carry
X-Collab-Admin: 1and come from the site's own origin.
- The prerender job has its own user and may write only
prerender/,data/andjobs/.- It cannot read the admin token or the secrets.
- It needs outbound HTTPS to the RPC endpoints and to api.coingecko.com.
- nginx never serves scripts,
config.jsor the admin page from its folders. A job that reads untrusted chain data, images included, can change neither the contracts the site uses nor its code.
The helper, sudo collab …:
| Command | |
|---|---|
admin-token [--new] | the admin token (a new one) |
status | release, site, deployment, prerender, content policy, certificate, services |
prerender [--request] | run the prerender job now, in the foreground (or ask the service for a run) |
logs [-f] [LINES] | the log of the admin service, the prerender job and the policy step |
apply | the content policy step |
renewed | certbot's deploy hook: reload nginx and note the new certificate |
Status, logs and the collab command shows what each prints.
8. Upgrades, backups and moving
Upgrade. Upload and extract the new archive as in section 2, and run its installer:
cd /var/www/html && tar xzf collab-2.2.1.tar.gz && sudo ./collab-2.2.1/server/install.shExtract each archive where no folder of the same name is left. tar over an older collab-2.2.1/
keeps the files the new archive no longer has, and the installer copies them into the release
with the rest, without checking them against SHA256SUMS. Delete such a folder first.
The settings, the token, the published deployment, the site settings and the certificate are kept,
and the two previous releases stay in releases/.
Going back. Run the older release's own copy of the installer, never ln -sfn:
sudo /var/www/collab/releases/2.2.0/server/install.shBackups. Back up /etc/collab and shared/site.json, config.js, deployments/ and
history/.
Upgrades, rollbacks and backups has the commands, what happens at each step, restoring and moving to another server.
9. Troubleshooting
| What you see | What to do |
|---|---|
| "Another site on this nginx answers for a name COLLAB would take" | That site's file is named. Remove the name from its server_name (and sudo nginx -t && sudo systemctl reload nginx), or install without it (--main, --alias, --no-aliases). Nothing was changed. |
| "Port 80 is used by another program" (or 443) | Another web server (Apache, Caddy) holds the port. Stop or reconfigure it, then run the installer again. |
| A name "points at …, not at this server", or the certificate leaves names out | Fix the DNS record (section 1) and run the same command again; the certificate is extended. Port 80 must be reachable from the internet for Let's Encrypt. |
| certbot could not get the certificate | The installer shows certbot's last lines; /var/log/letsencrypt/letsencrypt.log has the rest. Fix the cause before trying again: Let's Encrypt allows only a few failed validations an hour. |
| The site answers "COLLAB is being set up" (503) | There is no certificate for the main name yet (above). |
| The admin page says the admin service is not running | sudo systemctl status collab-admin, sudo collab logs. |
The installer can always be run again: it repairs what is missing and keeps what is there. Troubleshooting covers these and the other cases, from the prerender's warnings to a refused content policy.
10. Testing the installer
sudo server/tests/sandbox_install.sh runs the installer in a sandbox without touching the
system's nginx. It sets up:
- a private nginx on ports 8088 and 8443, with another site on it;
- a self-signed certificate;
- the admin service as user
collab.
Then it checks:
- the refusal while another site claims a name;
nginx -t, the redirects, headers, caching,/data/and the prerender overlay;- the admin API with its refusals and limits, and the content policy step;
deploy/check_site.py;- a second run that changes nothing, the same version again with other files, an upgrade, and a run from an installed release;
- the Let's Encrypt path, with certbot and DNS stubbed: one name without DNS, the deploy hook, and a next run that keeps the certificate.
python3 -m unittest discover -s server/tests runs the unit tests of the admin service. See
Testing.