Security overview
Summary
- Where data lives — in your PostgreSQL database, on your infrastructure. CHIPS sends no data to the vendor.
- Passwords — stored only as scrypt hashes.
- Sessions — random tokens, stored as SHA-256 hashes, expiring after 14 days (configurable). Signing out deletes them; an administrator can sign anyone out everywhere.
- Personal access tokens — shown once, stored as hashes, read-only or read/write, with expiry and revocation.
- Stored secrets — Jira tokens and OAuth client secrets are encrypted with a key derived from
STORAGE_SECRET. - Secret votes — votes are never sent to anyone's browser before the reveal; with hide values on, team members never receive values. Vote values are never written to the activity log.
- OAuth / OpenID Connect — authorization-code flow with PKCE and state; accounts are linked by email only when the provider has verified it.
- Jira — administrators can restrict which Jira hosts may be used; HTTPS is required by default; link-local addresses are refused.
- CSV downloads — cells that would run as spreadsheet formulas are neutralised.
- Audit — the activity log records sign-ins (including failures), administration and changes with the actor and IP address.
- Licensing — keys are Ed25519-signed and verified offline; the product contains only public keys.
Security measures
- Setup takeover is impossible without the setup code (the earlier "first account becomes the administrator" is gone).
- Two-factor sign-in (TOTP authenticator apps, RFC 6238) with 10 single-use recovery codes, optional
for everyone and required for administrators if the setting is on (administration is locked until it
is set up). Codes cannot be replayed; a sign-in challenge allows 5 codes and expires in 5 minutes.
Administrators can turn it off for a user (lost phone): Admin → Users, or
manage reset-2fa NAME. - Password guessing: 5 wrong passwords in 15 minutes lock the username (1 minute, doubling, at most
1 hour), 20 lock the network address (not counted for loopback and trusted proxies). Unlock: Admin →
Users or
manage unlock NAME. Unknown usernames take as long to refuse as wrong passwords. - Password policy: minimum length (default 10, configurable 8–64), no common passwords, not the username — for setup, registration, password changes, admin resets and the CLI.
- Storage: scrypt password hashes; sessions, invite codes, API tokens, recovery codes and the setup
code stored only as hashes; Jira tokens, OAuth client secrets and TOTP keys encrypted (Fernet) with a key
derived from
STORAGE_SECRET. CHIPS will not start with a missing or short secret (CHIPS_ALLOW_INSECURE=1for development only). - HTTP: Content-Security-Policy (
default-src 'self', no plug-ins, no framing, no foreign forms),X-Frame-Options: DENY,nosniff, referrer and permissions policies, COOP, HSTS when served over HTTPS,Cache-Control: no-storefor pages and API, noServerheader; session cookiesSameSite=Laxand HTTPS-only whenPUBLIC_URLishttps://(orCHIPS_SECURE_COOKIES=true). Error pages show only a reference; details are logged. Markdown from users and Jira is sanitised; CSV downloads neutralise formulas; OAuth uses PKCE and state; SQL is always parameterised. - Sandboxing: the systemd unit runs as
scrumpokerwith no capabilities,ProtectSystem=strict, private/tmpand devices, kernel protections, a system-call filter and only IP/Unix sockets, withUMask=0077; the Compose container is read-only, without capabilities or privilege gain. - Auditing: sign-ins, failures, locks, two-factor changes, setup and settings changes are in the activity log.
- Checks: the wizard, Admin → Settings and
manage security-checkreport the secret, HTTPS, secure cookies, the database and the installation's integrity.
Protected builds
Customers receive CHIPS compiled to native code, not as Python source
(packaging/protect.py, used by the release builder):
- every module is translated to C with Cython and compiled for Python 3.10, 3.11, 3.12 and 3.13 (one
package runs on all of them); docstrings are removed (except the REST API's, which are its
documentation) and symbols stripped; only two one-line entry stubs remain as
.py; scrumpoker/INTEGRITY.jsonlists the SHA-256 of every file and is signed with the vendor's Ed25519 key (the license signing key). At startup CHIPS verifies the signature, the files, that no file was added, that signature checking itself rejects a forgery, and that the license functions are still the compiled ones. If anything fails, CHIPS runs as Community, administrators see "This installation of CHIPS has been modified", and the activity log records what was found;- the license agreement (1.1) forbids reverse engineering and working around these measures.
Compiled modules are specific to the operating system and processor, so each platform's packages are built on that platform — see Building for every platform. No protection can make software that runs on a customer's computer impossible to analyse; this makes reading and modifying CHIPS much harder and detects modification.
Report security issues to [security@chips.example.com]; see the support policy.