11#! /usr/bin/env bash
2- # Generate NATS account + service user JWTs for LocalAI distributed mode.
2+ # Generate NATS JWT authentication material and server configuration
3+ # for LocalAI distributed mode.
34#
45# Requires: nsc (https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nsc)
56#
6- # Usage:
7- # ./scripts/nats-auth-setup.sh
7+ # Outputs:
8+ # ./nats-keys/localai-nats.env
9+ # ./nats-keys/localai-frontend.creds
10+ # ./nats-keys/nats-auth.conf
11+ # ./nats-keys/nats-server.conf
812#
9- # Outputs operator/account seeds and a service user JWT suitable for:
10- # LOCALAI_NATS_ACCOUNT_SEED
11- # LOCALAI_NATS_SERVICE_JWT
13+ # Environment overrides:
14+ # NATS_OPERATOR_NAME
15+ # NATS_ACCOUNT_NAME
16+ # NATS_SERVICE_USER
17+ # NATS_KEYS_DIR
18+
1219#
13- # Per-node worker JWTs are minted automatically by the frontend at registration
14- # when LOCALAI_NATS_ACCOUNT_SEED is set .
20+ # LocalAI workers receive their own JWT and user seed when registering
21+ # with the frontend .
1522
1623set -euo pipefail
1724
25+ # Ensure newly created secret files are private by default.
26+ umask 077
27+
1828if ! command -v nsc > /dev/null 2>&1 ; then
1929 echo " nsc is required. Install from https://github.com/nats-io/nsc/releases" >&2
2030 exit 1
2131fi
2232
2333OPERATOR=" ${NATS_OPERATOR_NAME:- localai-operator} "
2434ACCOUNT=" ${NATS_ACCOUNT_NAME:- localai} "
35+ SYSTEM_ACCOUNT=" ${NATS_SYSTEM_ACCOUNT_NAME:- SYS} "
2536SERVICE_USER=" ${NATS_SERVICE_USER:- localai-frontend} "
37+ OUTPUT_DIR=" ${NATS_KEYS_DIR:- ./ nats-keys} "
38+
39+ CREDS_FILE=" $OUTPUT_DIR /${SERVICE_USER} .creds"
40+ ENV_FILE=" $OUTPUT_DIR /localai-nats.env"
41+ AUTH_CONFIG_FILE=" $OUTPUT_DIR /nats-auth.conf"
42+ SERVER_CONFIG_FILE=" $OUTPUT_DIR /nats-server.conf"
43+
44+ mkdir -p " $OUTPUT_DIR "
45+
46+ echo " Configuring NATS operator: $OPERATOR "
47+
48+ # Create the operator if it does not exist, otherwise select it.
49+ if nsc select operator " $OPERATOR " > /dev/null 2>&1 ; then
50+ echo " [ OK ] using existing operator '$OPERATOR '"
51+ else
52+ nsc add operator \
53+ -n " $OPERATOR " \
54+ --generate-signing-key
55+
56+ nsc select operator " $OPERATOR " > /dev/null
57+ fi
58+
59+ # Create and assign the NATS system account.
60+ if nsc describe account \
61+ -n " $SYSTEM_ACCOUNT " > /dev/null 2>&1 ; then
62+ echo " [ OK ] using existing system account '$SYSTEM_ACCOUNT '"
63+ else
64+ nsc add account -n " $SYSTEM_ACCOUNT "
65+ fi
66+
67+ nsc edit operator \
68+ --system-account " $SYSTEM_ACCOUNT "
69+
70+ # Create the LocalAI account if it does not exist.
71+ if nsc describe account -n " $ACCOUNT " > /dev/null 2>&1 ; then
72+ echo " [ OK ] using existing account '$ACCOUNT '"
73+ else
74+ nsc add account -n " $ACCOUNT "
75+ fi
76+
77+ nsc select account " $ACCOUNT " > /dev/null
78+
79+ # Create the frontend service user if it does not exist.
80+ if nsc describe user \
81+ -n " $SERVICE_USER " \
82+ --account " $ACCOUNT " > /dev/null 2>&1 ; then
83+ echo " [ OK ] using existing user '$SERVICE_USER '"
84+ else
85+ nsc add user \
86+ -n " $SERVICE_USER " \
87+ --account " $ACCOUNT "
88+ fi
89+
90+ # Frontend control-plane permissions.
91+ nsc edit user \
92+ -n " $SERVICE_USER " \
93+ --account " $ACCOUNT " \
94+ --allow-pub " nodes.>,gallery.>,agent.>,staging.>,state.>,jobs.>,mcp.>,cache.>,prefixcache.>,finetune.>" \
95+ --allow-sub " nodes.>,gallery.>,agent.>,staging.>,state.>,jobs.>,mcp.>,cache.>,prefixcache.>,_INBOX.>"
96+
97+ # Generate a credentials file containing the frontend user JWT and seed.
98+ rm -f " $CREDS_FILE "
99+
100+ nsc generate creds \
101+ -a " $ACCOUNT " \
102+ -n " $SERVICE_USER " \
103+ -o " $CREDS_FILE "
104+
105+ # Extract the frontend JWT from the credentials file.
106+ SERVICE_JWT=" $(
107+ awk '
108+ /BEGIN NATS USER JWT/ {
109+ capture = 1
110+ next
111+ }
112+ /END NATS USER JWT/ {
113+ capture = 0
114+ }
115+ capture
116+ ' " $CREDS_FILE " | tr -d ' \r\n'
117+ ) "
118+
119+ # Extract the frontend user seed from the credentials file.
120+ SERVICE_SEED=" $(
121+ awk '
122+ /BEGIN USER NKEY SEED/ {
123+ capture = 1
124+ next
125+ }
126+ /END USER NKEY SEED/ {
127+ capture = 0
128+ }
129+ capture
130+ ' " $CREDS_FILE " | tr -d ' \r\n'
131+ ) "
132+
133+ # Retrieve the seed belonging to this exact account rather than taking
134+ # the first account key found in the keystore.
135+ ACCOUNT_SEED=" $(
136+ nsc list keys \
137+ --account " $ACCOUNT " \
138+ --accounts \
139+ --show-seeds |
140+ awk -F ' |' -v expected=" $ACCOUNT " '
141+ function trim(value) {
142+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
143+ return value
144+ }
145+
146+ NF >= 3 {
147+ entity = trim($2)
148+ seed = trim($3)
149+
150+ if (entity == expected && seed ~ /^SA[A-Z0-9]+$/) {
151+ print seed
152+ exit
153+ }
154+ }
155+ '
156+ ) "
157+
158+ # Validate all extracted values before writing output files.
159+ if [[ ! " $ACCOUNT_SEED " =~ ^SA[A-Z0-9]+$ ]]; then
160+ echo " Unable to extract the account seed for '$ACCOUNT '." >&2
161+ exit 1
162+ fi
163+
164+ if [[ ! " $SERVICE_JWT " =~ ^eyJ ]]; then
165+ echo " Unable to extract the service JWT from '$CREDS_FILE '." >&2
166+ exit 1
167+ fi
168+
169+ if [[ ! " $SERVICE_SEED " =~ ^SU[A-Z0-9]+$ ]]; then
170+ echo " Unable to extract the service seed from '$CREDS_FILE '." >&2
171+ exit 1
172+ fi
173+
174+ # Generate the trusted operator and memory resolver configuration.
175+ # This contains public operator/account JWT claims, not the private seeds.
176+ nsc generate config \
177+ --mem-resolver \
178+ --config-file " $AUTH_CONFIG_FILE " \
179+ --force
180+
181+ # Generate the primary NATS server configuration.
182+ # The include path matches the Docker Compose mounts shown below.
183+ cat > " $SERVER_CONFIG_FILE " << 'NATS_CONFIG '
184+ server_name: localai-nats
185+ port: 4222
186+ http: 8222
187+
188+ jetstream {
189+ store_dir: /data/jetstream
190+ }
191+
192+ include nats-auth.conf
193+ NATS_CONFIG
194+
195+ # Generate the environment file consumed by the LocalAI frontend.
196+ cat > " $ENV_FILE " << EOF
197+ LOCALAI_NATS_ACCOUNT_SEED=$ACCOUNT_SEED
198+ LOCALAI_NATS_SERVICE_JWT=$SERVICE_JWT
199+ LOCALAI_NATS_SERVICE_SEED=$SERVICE_SEED
200+ EOF
201+
202+ # The environment and credentials files contain private seeds.
203+ chmod 600 " $CREDS_FILE " " $ENV_FILE "
204+
205+ # These contain server configuration and public JWT claims.
206+ chmod 644 " $AUTH_CONFIG_FILE " " $SERVER_CONFIG_FILE "
26207
27- nsc add operator -n " $OPERATOR " --generate-signing-key
28- nsc add account -n " $ACCOUNT "
29- nsc add user -n " $SERVICE_USER " --account " $ACCOUNT "
30-
31- # Broad publish for frontend control plane (tighten with custom claims in production).
32- nsc edit user -n " $SERVICE_USER " --account " $ACCOUNT " \
33- --allow-pub " nodes.>,gallery.>,agent.>,jobs.>,mcp.>,cache.>,prefixcache.>,finetune.>" \
34- --allow-sub " nodes.>,gallery.>,agent.>,jobs.>,mcp.>,cache.>,prefixcache.>,_INBOX.>"
35-
36- KEYS_DIR=" ${NATS_KEYS_DIR:- ./ nats-keys} "
37- mkdir -p " $KEYS_DIR "
38- nsc generate creds -a " $ACCOUNT " -n " $SERVICE_USER " -o " $KEYS_DIR "
39-
40- ACCOUNT_SEED=$( nsc describe account " $ACCOUNT " -o json | jq -r ' .nats.private_key' )
41- SERVICE_JWT=$( cat " $KEYS_DIR /${ACCOUNT} /${SERVICE_USER} .jwt" 2> /dev/null || cat " $KEYS_DIR /${SERVICE_USER} .jwt" )
42-
43- echo " "
44- echo " === LocalAI NATS auth material ==="
45- echo " LOCALAI_NATS_ACCOUNT_SEED=${ACCOUNT_SEED} "
46- echo " LOCALAI_NATS_SERVICE_JWT=${SERVICE_JWT} "
47- echo " "
48- echo " Configure the NATS server with the generated operator/account JWTs under $KEYS_DIR "
49- echo " and set LOCALAI_NATS_REQUIRE_AUTH=true on frontends and workers in production."
208+ echo
209+ echo " === LocalAI NATS JWT setup complete ==="
210+ echo
211+ echo " LocalAI environment: $ENV_FILE "
212+ echo " Service credentials: $CREDS_FILE "
213+ echo " NATS server config: $SERVER_CONFIG_FILE "
214+ echo " NATS auth config: $AUTH_CONFIG_FILE "
215+ echo
216+ echo " Keep '$ENV_FILE ' and '$CREDS_FILE ' secret."
217+ echo " Do not commit them to source control."
218+ echo
219+ echo " === LocalAI NATS environment ==="
220+ cat " $ENV_FILE "
0 commit comments