Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Holistic

Commands

sudo ./holistic setup                 # system prep + all services
sudo ./holistic setup dashboard       # single service
sudo ./holistic user add <name>       # new user (Linux + Samba + groups)
sudo ./holistic user passwd <name>    # change password (Linux + Samba)
sudo ./holistic user delete <name>    # remove user
sudo ./holistic invite new            # mint a dashboard registration code
sudo ./holistic update                # git pull + re-run setup
./holistic list                       # services in install order
./holistic status                     # check install state

After holistic setup, the dashboard is at https://holistic.local. Users sign in with their Linux account; registration needs an invite new code.

Connect (Samba)

Platform Path
Windows \\<server-ip>\<username> or \\<server-ip>\family
macOS smb://<server-ip>/<username> or smb://<server-ip>/family
Linux smb://<server-ip>/<username> (GNOME) or mount via cifs-utils (below)

Linux (cifs-utils)

sudo apt install cifs-utils
sudo mkdir -p /mnt/holistic
sudo mount -t cifs //<server-ip>/<username> /mnt/holistic \
  -o user=<username>,uid=$(id -u),gid=$(id -g),iocharset=utf8

GNOME/Nautilus: gio mount smb://<server-ip>/<username> (or Files → Other Locations).

Persistent mount — keep the password in a root-only credentials file:

sudo install -m600 /dev/stdin /etc/holistic-smb.cred <<'CRED'
username=<username>
password=<your-password>
CRED
# /etc/fstab line:
# //<server-ip>/<username> /mnt/holistic cifs credentials=/etc/holistic-smb.cred,uid=1000,gid=1000,_netdev 0 0
sudo mount -a

Add a service

Create services/<name>/install.sh, add <name> to services/manifest. Runs as root, must be idempotent, must print [<name>] installed and started.

Runtime domain

holistic is portable: it has no hardcoded hostname and runs same-origin behind Caddy on whatever domain you point at it. The domain it is currently served on is resolved at runtime from the (Caddy-set) forwarded headers — GET /api/instance is the single source of truth:

{ "origin": "https://example.com", "host": "example.com", "mailDomain": "example.com" }
  • origin / host are per-request (LAN holistic.local, the public tunnel domain, dev localhost — whatever the client used). Use these for absolute links/redirects.
  • mailDomain is the stable, canonical domain for addresses like user@<mailDomain> (e.g. a future email service). It is learned once from the first public access and persisted to /var/lib/holistic/instance.json; LAN/loopback/IP hosts are ignored, so it is "" until a real domain is seen.

How a service consumes it:

  • UI plugin: read props.instance (InstanceInfo in @holistic/ui) — e.g. `${user.username}@${instance.mailDomain}`. If mailDomain is "", prompt the operator to set HOLISTIC_MAIL_DOMAIN.
  • Service daemon: read /var/lib/holistic/instance.json (shared state, like jwt-secret/permissions.d) or call /api/instance.

Overrides (env on the dashboard unit; empty = auto): HOLISTIC_MAIL_DOMAIN pins the canonical mail domain — set it when you want the apex (example.com) instead of the served host (holistic.example.com), or a different MX domain. HOLISTIC_PUBLIC_ORIGIN pins the full scheme://host (rare).

Declare permissions (rights standard)

Admin = the sudo group and can do everything. To grant non-admin users fine-grained rights, a service declares them through a holistic-wide standard; the privleg service then lets admins toggle them per user.

A service drops a manifest at install time to /etc/holistic/permissions.d/<id>.json (like its Caddy and sudoers drop-ins). Each right is backed 1:1 by a Linux group named hp_*; the service creates the groups (groupadd -f) and enforces the right itself with isAdmin || group ∈ user.groups:

{
  "service": "hostek", "version": 1,
  "categories": [{
    "id": "system", "label": "System",
    "permissions": [{
      "id": "power", "label": "Power control",
      "description": "Turn the server on/off, toggle headless",
      "group": "hp_hostek_power", "default": false, "dangerous": true
    }]
  }]
}

Rules: groups must match ^hp_[a-z0-9][a-z0-9_-]{0,27}$ (the hp_ prefix is a security boundary — privleg only ever touches hp_* groups) and each group backs exactly one right. Pick default so a host without privleg behaves exactly as today: default:false for an action that is admin-only now (group stays empty ⇒ admin-only; privleg grants it per user), or default:true for an action open to everyone now (provisioning grants it to all users ⇒ unchanged; privleg revokes it per user). Enforce with isAdmin || group ∈ user.groups either way. The TypeScript shapes are PermissionManifest/PermissionCategory/PermissionDecl in @holistic/ui. Validate with holistic perms validate.

Declare configuration (config standard)

The exact mirror of the rights standard, for settings instead of rights. All configuration — anything an admin sets — is bundled in the dashboard's single Configuration tab, never in a service's own tab: a service tab belongs to the user's experience and must not drown in knobs.

A service declares its settings and their defaults in a manifest dropped at install time to /etc/holistic/config.d/<id>.json (like its permissions drop-in):

{
  "service": "hosuto", "version": 1,
  "categories": [{
    "id": "limits", "label": "Limits",
    "settings": [
      { "id": "maxServersPerUser", "label": "Servers per member", "type": "int", "default": 3 },
      { "id": "loader", "label": "Default loader", "type": "enum",
        "default": "paper", "options": ["vanilla", "fabric", "paper"] }
    ]
  }]
}

An admin edits the values there; the dashboard validates them against the manifest and writes /var/lib/holistic/config/<id>.json atomically (mode 0640, group holistic). Every service daemon simply reads that file live — no restart, no RPC — and falls back to its declared default for anything absent, so a host that has never been configured behaves exactly as before.

Rules: the service id must match ^[a-z][a-z0-9]{2,19}$ and equal the file stem (it is the same id as the plugin and the permissions manifest); setting ids match ^[a-z][a-zA-Z0-9_]*$ and are unique per service (the daemon reads them by bare id); type is one of string, int, bool, enum, secret; an enum needs a non-empty options[] containing its default; dangerous: true makes the editor confirm before saving. Validate with holistic config validate.

Layout

holistic                        CLI entry point
services/manifest               install order
services/<name>/install.sh      per-service setup
services/<name>/ui/             optional dashboard UI (a @holistic/ui plugin)
frontend/                       dashboard SPA + @holistic/ui SDK

About

No description or website provided.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages