Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
#+title: Bits #+startup: content [[https://code.invetica.team/jcf/bits/actions][https://code.invetica.team/jcf/bits/badges/workflows/ci.yml/badge.svg]] Platforms control your audience. Payment processors control your revenue. Bits gives you both back. *What you get:* - Subscriptions, one-time purchases, digital downloads - Video hosting and delivery - Your data, your domain, your rules - Permissive content policies for persecuted creators *Why it works:* - Self-host on a $5/month VPS or use managed hosting - Single binary deployment - no complex setup - Built with care to minimize resource usage and maximize reliability - Source-available - audit the code, modify what you need, contribute back *The model:* Like WordPress.org - free to self-host, managed option available. You're never locked in. *Current state:* Active development. Your funding accelerates delivery. * CLI #+begin_src sh :results output verbatim just cli --no-color --help 2>&1 #+end_src #+results: : Usage: bits <command> [options] : : Commands: : seed Apply database seeds : serve Start the HTTP server : warmup Load classes for AppCDS generation : : Run 'bits <command> --help' for command-specific help. * Development #+begin_src sh :eval never just setup # First time: generates certs, builds container images just dev # Starts all services via Docker Compose #+end_src All infrastructure runs in Docker containers (built with Nix). The =just dev= command starts PostgreSQL, Datomic transactor, Jaeger, Traefik, the Clojure nREPL, and Tailwind CSS watcher. ** HTTPS and =*.localhost= URLs Subdomains of =localhost= automatically resolve to =127.0.0.1= per RFC 6761 — no custom DNS resolver needed. Traefik terminates TLS with mkcert certificates. *** Test The marketing site and "catch-all" homepage where we have no realm aren't a priority so will return non-200 status codes. #+begin_src sh :results output verbatim :shebang "#!/usr/bin/env zsh" urls=( # These will work, provided you've applied the seeds in `bits.dev.realm`. https://bits.page.localhost https://charlie.bits.page.localhost https://jcf.bits.page.localhost ) for url in $urls; do http_code="$(curl --silent --output /dev/null --write-out "%{http_code}" "$url")" if [ "$http_code" -eq 200 ]; then echo "✅ $url" else echo "❌ $url ($http_code)" fi done #+end_src #+results: : ✅ https://bits.page.localhost : ✅ https://charlie.bits.page.localhost : ✅ https://jcf.bits.page.localhost And with a non-existent tenant, we expect a 404 response: #+begin_src sh :results output verbatim url="https://foo.bits.page.localhost" http_code="$(curl --silent --output /dev/null --write-out "%{http_code}" "$url")" if [ "$http_code" -eq 404 ]; then echo "✅ $url" else echo "❌ $url ($http_code)" fi #+end_src #+results: : ✅ https://foo.bits.page.localhost ** Decision records #+begin_src sh :results output raw :exports output :exports results fd --type file '\.org$' decisions | while read -r file; do title=$(awk -F':[[:space:]]*' 'NR==1 { print $2 }' "$file") echo "- [[file:$file][$title]]" done #+end_src #+results: - [[file:decisions/20251009133634-get-the-message-out.org][Get the message out]] - [[file:decisions/20251009144920-python-style-environment-variables.org][Python-style environment variables]] - [[file:decisions/20251118100535-mission-over-saas-startup-playbook.org][Mission over SaaS startup playbook]] - [[file:decisions/20251119230950-ci-can-wait.org][CI can wait]] - [[file:decisions/20251129175326-clojure-over-rust.org][Clojure over Rust]] - [[file:decisions/20260212183128-vanilla-js-over-clojurescript-for-bits-js.org][Vanilla JS over ClojureScript for bits.js]] - [[file:decisions/20260214190732-datahike-vs-datomic-for-distributed-coordination-appendix-crypto-shredding.org][Crypto-Shredding for GDPR Compliance]] - [[file:decisions/20260214190732-datahike-vs-datomic-for-distributed-coordination.org][Datahike vs Datomic for distributed coordination]] - [[file:decisions/20260220174238-body-style-indentation-for-ui-component-functions.org][Body-style indentation for UI component functions]] ** Dev tasks #+begin_src sh :results output verbatim :exports results just --list #+end_src ** SLOC #+begin_src sh :results output verbatim :exports results tokei echo echo "$(git describe --always --dirty --abbrev=7) @ $(date +"%Y/%m/%d %H:%M")" #+end_src #+results: #+begin_example ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Language Files Lines Code Comments Blanks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Astro 10 165 139 0 26 Clojure 66 6751 5330 505 916 CSS 2 84 69 4 11 Dockerfile 1 82 58 7 17 Edn 2 136 121 1 14 HCL 12 386 320 13 53 JavaScript 5 305 250 20 35 JSON 4 59 59 0 0 Just 1 292 181 61 50 MDX 1 56 0 39 17 Nix 11 1131 861 122 148 Org 15 2335 2039 5 291 SQL 4 43 37 0 6 SVG 2 12 12 0 0 TOML 1 43 38 0 5 TypeScript 3 36 30 1 5 XML 1 18 14 1 3 YAML 3 5073 3988 0 1085 ───────────────────────────────────────────────────────────────────────────────── HTML 1 76 72 0 4 |- CSS 1 171 151 0 20 (Total) 247 223 0 24 ───────────────────────────────────────────────────────────────────────────────── Markdown 1 404 0 266 138 |- Clojure 1 238 152 50 36 |- Org 1 19 12 0 7 |- Rust 1 68 49 10 9 |- YAML 1 1 1 0 0 (Total) 730 214 326 190 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Total 146 17984 13983 1105 2896 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 83a623b @ 2026/02/26 23:46 #+end_example * License Bits is source-available under the Business Source License 1.1 (BUSL-1.1) and converts to the GNU Affero General Public License v3.0 or later four years after each version's first public distribution. A commercial license is available for organisations that cannot comply with BUSL. See [[file:LICENSING.org][LICENSING.org]] for terms, rationale, and how to obtain a commercial license. * TODOs #+begin_src sh :results output verbatim :exports results rg --glob '!README.org' 'TODO|FIXME' </dev/null | sort #+end_src #+results: #+begin_example src/bits/app.clj:;; TODO Use Malli (or clojure.spec) to coerce and parse/validate configuration. src/bits/asset.clj:;; TODO Make content-type explicit or more intelligent. src/bits/brotli.clj: ;; TODO: Default buffer size for brotli library, needs to be tuned. src/bits/middleware.clj: ;; TODO Rename :sid to :session/id src/bits/module/creator.clj: ;; TODO: Real data from database src/bits/module/creator.clj: ;; TODO Extract platform/realm domain src/bits/morph.clj:;; FIXME Relocate `morphable`. src/bits/schema.clj: ;; FIXME Avoid stringly typed entities - use `:db/ident`. src/bits/ui.clj: ;; TODO: I18n - error presentation test/bits/session_test.clj: ;; FIXME Remove `:sid` from `data`. #+end_example