@aifabrix/server-setup 1.3.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -19
- package/assets/aifabrix-apply-dev-users.sh +123 -0
- package/assets/setup-dev-server-no-node.sh +216 -76
- package/assets/setup-install-init.sh +42 -0
- package/dist/backup.spec.js +1 -1
- package/dist/cli.js +66 -3
- package/dist/install-init.d.ts +6 -0
- package/dist/install-init.js +41 -0
- package/dist/install-init.spec.d.ts +1 -0
- package/dist/install-init.spec.js +10 -0
- package/dist/install-ssh.d.ts +10 -0
- package/dist/install-ssh.js +39 -0
- package/dist/install-ssh.spec.d.ts +4 -0
- package/dist/install-ssh.spec.js +55 -0
- package/dist/install.d.ts +3 -0
- package/dist/install.js +73 -8
- package/dist/install.spec.d.ts +1 -0
- package/dist/install.spec.js +23 -0
- package/dist/ssh.d.ts +1 -0
- package/dist/ssh.js +104 -11
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ This is the **one document** you need to get your builder-server **from zero to
|
|
|
17
17
|
**The builder-server Docker image is not on a public registry.** You get it with the **AI Fabrix platform**.
|
|
18
18
|
|
|
19
19
|
1. Install the **AI Fabrix Builder** CLI: `npm install -g @aifabrix/builder`
|
|
20
|
-
2.
|
|
20
|
+
2. Install the **AI Fabrix server-setup** CLI (af-server): `npm install -g @aifabrix/server-setup`
|
|
21
|
+
3. Use the Builder CLI to run or deploy the platform (including builder-server). See [AI Fabrix Builder](https://github.com/esystemsdev/aifabrix-builder) and [docs/README.md](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/README.md).
|
|
21
22
|
|
|
22
23
|
Once you have the platform (and the builder-server image), use **af-server** to install that server on your own host.
|
|
23
24
|
|
|
@@ -39,19 +40,95 @@ Do the steps in [What you must do before running af-server](#what-you-must-do-be
|
|
|
39
40
|
|
|
40
41
|
## Install: from zero to running
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
Complete the [manual prerequisites](#what-you-must-do-before-running-af-server) first (DNS, SSL directory, certificate and key on the server).
|
|
44
|
+
|
|
45
|
+
**Flow summary:** Only **step 1** runs over SSH from your PC. Steps **5** and **7** run **on the server** after you log in, so errors and output are visible directly there.
|
|
46
|
+
|
|
47
|
+
| Step | Where | Action |
|
|
48
|
+
| ---- | --------- | ------ |
|
|
49
|
+
| 1 | From PC | `af-server install-init $SSH` — only command over SSH; installs on server: SSH (if needed), Node 18+, npm, and `af-server` CLI. |
|
|
50
|
+
| 2 | One-time | Log in to the server once (e.g. with password) to approve passwordless SSH; then from PC: `af-server ssh-cert install $SSH`. |
|
|
51
|
+
| 3 | From PC | Copy SSL certificate and key to the server (see [SSL directory and certificates](#ssl-directory-and-certificates)); example commands below. |
|
|
52
|
+
| 4 | From PC | Log in to the server via SSH (passwordless). |
|
|
53
|
+
| 5 | **On server** | `sudo af-server install` — install all services (Docker, nginx package, Mutagen, cron, data dir); no builder-server container yet. |
|
|
54
|
+
| 6 | On server | Get the builder-server image (e.g. `az login` / `docker pull`). |
|
|
55
|
+
| 7 | **On server** | `sudo af-server install-server --dev-domain $DOMAIN` — nginx vhost, builder-server container, Docker TLS. |
|
|
56
|
+
| 8 | — | Done. |
|
|
57
|
+
|
|
58
|
+
### Step 1: Bootstrap the server (from PC)
|
|
59
|
+
|
|
60
|
+
Set your target and run the only command that uses SSH from your PC:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export SSH=serveradmin@builder02.aifabrix.dev
|
|
64
|
+
export DOMAIN=builder02.aifabrix.dev
|
|
65
|
+
af-server install-init $SSH
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This installs on the server: openssh-server (if needed), Node 18+, npm, and `@aifabrix/builder` + `@aifabrix/server-setup` so `af-server` is available there. No Docker, nginx, or builder-server yet.
|
|
69
|
+
|
|
70
|
+
### Step 2: Passwordless SSH (from PC)
|
|
71
|
+
|
|
72
|
+
Log in to the server once (e.g. with password) to accept the host key and/or approve auth. Then from your PC:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
af-server ssh-cert install $SSH
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Step 3: Copy SSL (from PC)
|
|
79
|
+
|
|
80
|
+
Put `wildcard.crt` and `wildcard.key` in `/opt/aifabrix/ssl` on the server. Example (replace `$HDD` with the folder on your PC that has the cert and key):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
export HDD=/workspace/aifabrix-setup/certificates
|
|
84
|
+
ssh $SSH "sudo mkdir -p /opt/aifabrix/ssl"
|
|
85
|
+
scp $HDD/wildcard.crt $SSH:/tmp/wildcard.crt
|
|
86
|
+
scp $HDD/wildcard.key $SSH:/tmp/wildcard.key
|
|
87
|
+
ssh $SSH "sudo mv /tmp/wildcard.crt /tmp/wildcard.key /opt/aifabrix/ssl/ && sudo chmod 600 /opt/aifabrix/ssl/wildcard.key"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 4: Log in to the server
|
|
43
91
|
|
|
44
92
|
```bash
|
|
45
|
-
|
|
93
|
+
ssh $SSH
|
|
46
94
|
```
|
|
47
95
|
|
|
48
|
-
|
|
96
|
+
### Step 5: Install services (on server)
|
|
49
97
|
|
|
50
98
|
```bash
|
|
51
|
-
af-server install
|
|
99
|
+
sudo af-server install
|
|
52
100
|
```
|
|
53
101
|
|
|
54
|
-
|
|
102
|
+
This installs Docker, nginx (package only), Mutagen, data dir, apply-dev-users script and cron. It does **not** write the builder nginx vhost or start the builder-server container.
|
|
103
|
+
|
|
104
|
+
### Step 6: Get the builder-server image (on server)
|
|
105
|
+
|
|
106
|
+
The image is not on a public registry. Use your platform’s method (e.g. Azure CLI or Docker login), then pull. Example:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
az login
|
|
110
|
+
az acr login --name aifabrixdevacr
|
|
111
|
+
docker pull aifabrixdevacr.azurecr.io/aifabrix/builder-server:latest
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Or with Docker login (username/password from your registry):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
docker login <registry> -u <user> -p <password>
|
|
118
|
+
docker pull <registry>/aifabrix/builder-server:latest
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Step 7: Install server (nginx vhost + container) (on server)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
sudo af-server install-server --dev-domain $DOMAIN
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use the same domain as your DNS and SSL. Optional: `--ssl-dir /opt/aifabrix/ssl`, `--data-dir /opt/aifabrix/builder-server/data`, `--builder-port 3000`.
|
|
128
|
+
|
|
129
|
+
### Step 8: Done
|
|
130
|
+
|
|
131
|
+
Your builder-server is up. Use the **AI Fabrix Builder CLI** for users, secrets, certs, etc.—see [Builder documentation](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/developer-isolation.md).
|
|
55
132
|
|
|
56
133
|
---
|
|
57
134
|
|
|
@@ -59,11 +136,14 @@ After install, your builder-server is up. Use the **AI Fabrix Builder CLI** for
|
|
|
59
136
|
|
|
60
137
|
| Command | Description |
|
|
61
138
|
| -------- | ----------- |
|
|
62
|
-
| `af-server install
|
|
139
|
+
| `af-server install-init <user@host> [ -i SSH_KEY ]` | **From PC only.** One-time bootstrap over SSH: install on server SSH (if needed), Node 18+, npm, and af-server CLI. |
|
|
140
|
+
| `af-server install [ user@host ] [ -d DATA_DIR ] [ --dev-domain DOMAIN ] [ --ssl-dir PATH ] [ -i SSH_KEY ]` | **Run on server** (omit target): `sudo af-server install`. Infra only: Docker, nginx pkg, Mutagen, data dir, cron. No builder vhost or container. With target: same infra over SSH. |
|
|
141
|
+
| `af-server install-server --dev-domain DOMAIN [ -d DATA_DIR ] [ --ssl-dir PATH ] [ --builder-port PORT ]` | **On server only.** Nginx vhost, builder-server container, Docker TLS. Run after `sudo af-server install`. |
|
|
63
142
|
| `af-server backup [ user@host ] [ -d DATA_DIR ] [ -o output.zip ] [ -i SSH_KEY ]` | On-demand backup (config + DB + keys). |
|
|
64
143
|
| `af-server backup [ user@host ] --schedule [ --backup-dir PATH ] [ --keep-days N ] [ -i SSH_KEY ]` | Cron backup (daily 02:00, keep last N, default 7). |
|
|
65
144
|
| `af-server restore backup.zip [ user@host ] [ -d DATA_DIR ] [ --force ] [ -i SSH_KEY ]` | Restore backup to DATA_DIR. |
|
|
66
145
|
| `af-server ssh-cert install [ user@host ] [ -i SSH_KEY ]` | Add your SSH public key to server (passwordless auth). |
|
|
146
|
+
| `af-server install-ssh [ user@host ] [ -i SSH_KEY ]` | Activate SSH server (install openssh-server, enable and start ssh) without login. Omit target for local. |
|
|
67
147
|
|
|
68
148
|
Backups contain secrets; store encrypted. Cron backup needs SQLite (`builder.db`) and `zip` on the server; default backup dir: `/opt/aifabrix/backups`.
|
|
69
149
|
|
|
@@ -114,18 +194,23 @@ If you SSH as a non-root user, that user must be able to sudo. The script will a
|
|
|
114
194
|
|
|
115
195
|
---
|
|
116
196
|
|
|
117
|
-
## High level: what
|
|
197
|
+
## High level: what install vs install-server does
|
|
198
|
+
|
|
199
|
+
**`af-server install`** (step 5 — run on the server) does **infra only**:
|
|
200
|
+
|
|
201
|
+
- **System** — `apt update` and `apt upgrade`; optional hostname if `SETUP_HOSTNAME` is set.
|
|
202
|
+
- **Docker** — Installs Docker if missing; enables and starts it.
|
|
203
|
+
- **Admin user** — Adds the admin user (default `serveradmin`) to the `docker` group and grants passwordless sudo.
|
|
204
|
+
- **Nginx** — Installs the nginx **package** only; enables and starts it. Does **not** write the builder vhost yet.
|
|
205
|
+
- **Data dir** — Creates the data directory (default `/opt/aifabrix/builder-server/data`), workspace and ssh-keys subdirs, ownership for the container.
|
|
206
|
+
- **Apply-dev-users** — Installs the script and cron job (every 2 minutes) that sync per-developer OS users from builder-server state.
|
|
207
|
+
- **Mutagen** — Downloads and installs Mutagen; systemd service and daemon.
|
|
208
|
+
- **Optional** — If `INSTALL_PORTAINER=1`, installs the Portainer container.
|
|
118
209
|
|
|
119
|
-
|
|
210
|
+
**`af-server install-server`** (step 7 — run on the server) does the **server phase**:
|
|
120
211
|
|
|
121
|
-
- **
|
|
122
|
-
- **
|
|
123
|
-
- **
|
|
124
|
-
- **Nginx** — Installs nginx if missing; enables and starts it. Generates a site config for your domain that proxies HTTPS to the builder-server container (using `wildcard.crt` and `wildcard.key` from your SSL dir).
|
|
125
|
-
- **Builder-server data dir** — Creates the data directory (default `/opt/aifabrix/builder-server/data`), sets ownership for the container. Starts the `builder-server` container if the image already exists on the server; otherwise prints how to build and run it (you get the image from the AI Fabrix platform).
|
|
126
|
-
- **Sync user** — Creates a system user (default `aifabrix-sync`) for Mutagen SSH sync; home under the data dir; creates `.ssh` and `authorized_keys`.
|
|
127
|
-
- **Cron job** — Installs a cron job (every 2 minutes) that copies the managed `authorized_keys` file into the sync user’s `.ssh/authorized_keys`.
|
|
128
|
-
- **Mutagen** — Downloads and installs the Mutagen binary; creates a systemd service and starts the daemon.
|
|
129
|
-
- **Optional** — If `INSTALL_PORTAINER=1`, installs the Portainer container. If Docker TLS is not skipped, writes `/etc/docker/daemon.json`; you can use the **same certificate** from `/opt/aifabrix/ssl` (e.g. symlink or copy `wildcard.crt` and `wildcard.key` to the paths Docker expects: `/etc/docker/server-cert.pem`, `/etc/docker/server-key.pem`, and if needed `ca.pem` for the CA), or provide separate Docker TLS certs.
|
|
212
|
+
- **Nginx vhost** — Writes the builder site config from template (domain, SSL dir, proxy to builder-server), reloads nginx.
|
|
213
|
+
- **Builder-server container** — Creates data dir (if needed), starts the builder-server container (if the image is present).
|
|
214
|
+
- **Docker TLS** — Copies certs and configures `/etc/docker/daemon.json` for TLS (using website cert and builder-server CA).
|
|
130
215
|
|
|
131
|
-
Result:
|
|
216
|
+
Result: after both steps, the server has Docker, nginx (HTTPS for your domain), the builder-server container (if image present), and Mutagen—ready for the Builder CLI to use.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Apply per-developer OS users from builder-server state (DATA_DIR/ssh-keys, pending-removals).
|
|
3
|
+
# Run via cron every 2 minutes. Creates/updates dev<id> users, .ssh/authorized_keys, workspace symlink,
|
|
4
|
+
# and optional ~/.aifabrix/config.yaml when missing. Processes pending-removals then applies keys.
|
|
5
|
+
# Requires root. DATA_DIR must match builder-server (e.g. /opt/aifabrix/builder-server/data).
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
DATA_DIR="${DATA_DIR:-/opt/aifabrix/builder-server/data}"
|
|
10
|
+
SSH_KEYS_DIR="${DATA_DIR}/ssh-keys"
|
|
11
|
+
PENDING_REMOVALS="${DATA_DIR}/pending-removals"
|
|
12
|
+
# Defaults matching builder-server env.template (override via env or apply-dev-users-defaults)
|
|
13
|
+
AIFABRIX_SECRETS="${AIFABRIX_SECRETS:-/aifabrix-miso/builder/secrets.local.yaml}"
|
|
14
|
+
AIFABRIX_ENV_CONFIG="${AIFABRIX_ENV_CONFIG:-aifabrix-miso/builder/env-config.yaml}"
|
|
15
|
+
|
|
16
|
+
if [ ! -d "$DATA_DIR" ]; then
|
|
17
|
+
echo "DATA_DIR not found: $DATA_DIR"
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Read secrets-encryption key from server data dir (same as builder-server ENCRYPTION_KEY_PATH); do not log.
|
|
22
|
+
secrets_encryption_value=""
|
|
23
|
+
if [ -f "${DATA_DIR}/secrets-encryption.key" ]; then
|
|
24
|
+
secrets_encryption_value=$(cat "${DATA_DIR}/secrets-encryption.key" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# --- 1. Process removals ---
|
|
28
|
+
if [ -f "$PENDING_REMOVALS" ]; then
|
|
29
|
+
while IFS= read -r dev_id || [ -n "$dev_id" ]; do
|
|
30
|
+
dev_id=$(echo "$dev_id" | tr -d '\r\n ')
|
|
31
|
+
[ -z "$dev_id" ] && continue
|
|
32
|
+
user_name="dev${dev_id}"
|
|
33
|
+
if getent passwd "$user_name" >/dev/null 2>&1; then
|
|
34
|
+
userdel -r "$user_name" 2>/dev/null || userdel "$user_name" 2>/dev/null || true
|
|
35
|
+
fi
|
|
36
|
+
done < "$PENDING_REMOVALS"
|
|
37
|
+
: > "$PENDING_REMOVALS"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# --- 2. Process each developer that has keys ---
|
|
41
|
+
for key_file in "${SSH_KEYS_DIR}"/*/authorized_keys; do
|
|
42
|
+
[ -f "$key_file" ] || continue
|
|
43
|
+
dev_id=$(dirname "$key_file" | xargs basename)
|
|
44
|
+
[ -z "$dev_id" ] && continue
|
|
45
|
+
user_name="dev${dev_id}"
|
|
46
|
+
home_dir="/home/${user_name}"
|
|
47
|
+
workspace_target="${DATA_DIR}/workspace/${user_name}"
|
|
48
|
+
|
|
49
|
+
if ! getent passwd "$user_name" >/dev/null 2>&1; then
|
|
50
|
+
useradd -m -s /bin/bash "$user_name"
|
|
51
|
+
else
|
|
52
|
+
# Ensure shell is bash (e.g. after manual changes)
|
|
53
|
+
current_shell=$(getent passwd "$user_name" | cut -d: -f7)
|
|
54
|
+
if [ "$current_shell" != "/bin/bash" ]; then
|
|
55
|
+
usermod -s /bin/bash "$user_name" 2>/dev/null || true
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
mkdir -p "${home_dir}/.ssh"
|
|
60
|
+
cp "$key_file" "${home_dir}/.ssh/authorized_keys"
|
|
61
|
+
chown -R "${user_name}:${user_name}" "${home_dir}/.ssh"
|
|
62
|
+
chmod 700 "${home_dir}/.ssh"
|
|
63
|
+
chmod 600 "${home_dir}/.ssh/authorized_keys"
|
|
64
|
+
|
|
65
|
+
# .aifabrix/config.yaml only if missing (do not overwrite)
|
|
66
|
+
config_dir="${home_dir}/.aifabrix"
|
|
67
|
+
config_file="${config_dir}/config.yaml"
|
|
68
|
+
if [ ! -f "$config_file" ]; then
|
|
69
|
+
mkdir -p "$config_dir"
|
|
70
|
+
hostname_val=$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo "localhost")
|
|
71
|
+
# Values from builder-server: secrets-encryption from DATA_DIR, paths from env (apply-dev-users-defaults or env.template)
|
|
72
|
+
yaml_escape() { echo "$1" | sed 's/\\/\\\\/g;s/"/\\"/g'; }
|
|
73
|
+
{
|
|
74
|
+
echo "user-mutagen-folder: ${workspace_target}"
|
|
75
|
+
printf "secrets-encryption: \"%s\"\n" "$(yaml_escape "$secrets_encryption_value")"
|
|
76
|
+
printf "aifabrix-secrets: \"%s\"\n" "$(yaml_escape "$AIFABRIX_SECRETS")"
|
|
77
|
+
printf "aifabrix-env-config: \"%s\"\n" "$(yaml_escape "$AIFABRIX_ENV_CONFIG")"
|
|
78
|
+
echo "remote-server: \"http://localhost:3000\""
|
|
79
|
+
echo "docker-endpoint: \"tcp://${hostname_val}:2376\""
|
|
80
|
+
echo "sync-ssh-user: \"${user_name}\""
|
|
81
|
+
echo "sync-ssh-host: \"${hostname_val}\""
|
|
82
|
+
} > "$config_file"
|
|
83
|
+
chown -R "${user_name}:${user_name}" "$config_dir"
|
|
84
|
+
chmod 700 "$config_dir"
|
|
85
|
+
chmod 600 "$config_file"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Workspace: ensure dir exists, owned by user; symlink from home
|
|
89
|
+
mkdir -p "$workspace_target"
|
|
90
|
+
chown -R "${user_name}:${user_name}" "$workspace_target"
|
|
91
|
+
if [ -L "${home_dir}/workspace" ]; then
|
|
92
|
+
current_target=$(readlink -f "${home_dir}/workspace" 2>/dev/null || true)
|
|
93
|
+
want_target=$(readlink -f "$workspace_target" 2>/dev/null || echo "$workspace_target")
|
|
94
|
+
if [ -n "$current_target" ] && [ -n "$want_target" ] && [ "$current_target" != "$want_target" ]; then
|
|
95
|
+
rm -f "${home_dir}/workspace"
|
|
96
|
+
ln -s "$workspace_target" "${home_dir}/workspace"
|
|
97
|
+
chown -h "${user_name}:${user_name}" "${home_dir}/workspace"
|
|
98
|
+
fi
|
|
99
|
+
elif [ ! -e "${home_dir}/workspace" ]; then
|
|
100
|
+
ln -s "$workspace_target" "${home_dir}/workspace"
|
|
101
|
+
chown -h "${user_name}:${user_name}" "${home_dir}/workspace"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Default directory on SSH login: cd to workspace
|
|
105
|
+
profile="${home_dir}/.profile"
|
|
106
|
+
if [ ! -f "$profile" ]; then
|
|
107
|
+
touch "$profile"
|
|
108
|
+
chown "${user_name}:${user_name}" "$profile"
|
|
109
|
+
fi
|
|
110
|
+
if ! grep -q 'cd.*workspace' "$profile" 2>/dev/null; then
|
|
111
|
+
echo '[ -d "$HOME/workspace" ] && cd "$HOME/workspace"' >> "$profile"
|
|
112
|
+
chown "${user_name}:${user_name}" "$profile"
|
|
113
|
+
fi
|
|
114
|
+
bashrc="${home_dir}/.bashrc"
|
|
115
|
+
if [ ! -f "$bashrc" ]; then
|
|
116
|
+
touch "$bashrc"
|
|
117
|
+
chown "${user_name}:${user_name}" "$bashrc"
|
|
118
|
+
fi
|
|
119
|
+
if ! grep -q 'cd.*workspace' "$bashrc" 2>/dev/null; then
|
|
120
|
+
echo '[ -d "$HOME/workspace" ] && cd "$HOME/workspace"' >> "$bashrc"
|
|
121
|
+
chown "${user_name}:${user_name}" "$bashrc"
|
|
122
|
+
fi
|
|
123
|
+
done
|