homelab infrastructure services
at main 594 lines 25 kB view raw
1#!/bin/bash 2 3set -euo pipefail 4 5# tinsnip installer - Downloads and sets up tinsnip 6 7REPO_URL="${REPO_URL:-https://tangled.sh/dynamicalsystem.com/tinsnip}" 8BRANCH="main" 9INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/opt/dynamicalsystem.tinsnip}" 10INSTALLER_VERSION="e3981a2" # Updated automatically on commit via hooks 11 12# Service repository options 13SERVICE_REPO_URL="${SERVICE_REPO_URL:-https://tangled.org/@dynamicalsystem.com/service}" 14SERVICE_INSTALL_DIR="${SERVICE_INSTALL_DIR:-$HOME/.local/opt/dynamicalsystem.service}" 15 16log() { 17 echo "[Installer] $*" 18} 19 20error() { 21 log "ERROR: $*" >&2 22 exit 1 23} 24 25download_file() { 26 local file_path="$1" 27 local dest_path="$2" 28 29 # Support both custom REPO_URL and default git server 30 if [[ -n "${REPO_URL_OVERRIDE:-}" ]] || [[ "$REPO_URL" != "https://tangled.sh/dynamicalsystem.com/tinsnip" ]]; then 31 local url="${REPO_URL}/${file_path}" 32 else 33 local url="${REPO_URL}/raw/${BRANCH}/${file_path}" 34 fi 35 36 local filename=$(basename "$file_path") 37 38 log " $filename (from $url)" 39 40 if command -v curl &> /dev/null; then 41 if curl -fsSL "$url" -o "$dest_path" 2>/dev/null; then 42 chmod +x "$dest_path" 2>/dev/null || true 43 return 0 44 fi 45 fi 46 47 error "Failed to download $file_path" 48} 49 50clone_with_git() { 51 log "Cloning repository with git..." 52 if ! command -v git &> /dev/null; then 53 log "Git not found, installing..." 54 sudo apt-get update -qq && sudo apt-get install -y git 55 fi 56 57 if ! git clone "git@tangled.sh:dynamicalsystem.com/tinsnip" "$INSTALL_DIR"; then 58 error "Failed to clone repository. Make sure you have SSH access to git@tangled.sh" 59 fi 60} 61 62# Clone or download service repository 63install_service_catalog() { 64 local service_repo="$SERVICE_REPO_URL" 65 66 log "Installing service catalog..." 67 log " Repository: $service_repo" 68 69 if [[ -d "$SERVICE_INSTALL_DIR" ]]; then 70 log " Removing: $SERVICE_INSTALL_DIR" 71 rm -rf "$SERVICE_INSTALL_DIR" 72 fi 73 74 mkdir -p "$(dirname "$SERVICE_INSTALL_DIR")" 75 76 # Try git clone first 77 if command -v git &> /dev/null; then 78 # Convert HTTPS URL to git URL for cloning 79 local git_url="${service_repo/https:\/\/tangled.sh\//git@tangled.sh:}" 80 81 if git clone "$git_url" "$SERVICE_INSTALL_DIR" 2>/dev/null; then 82 log " Service catalog installed: $SERVICE_INSTALL_DIR" 83 return 0 84 fi 85 fi 86 87 # Fallback to downloading files if git fails 88 log "Git clone failed, downloading service files..." 89 # For now, skip download fallback as service repo structure may vary 90 log "WARNING: Could not clone service repository. Services will not be available." 91 log "You can manually clone the service repository later:" 92 log " git clone $service_repo $SERVICE_INSTALL_DIR" 93} 94 95# Download all local services from ./service/ directory 96download_local_services() { 97 log "Discovering and downloading local services..." 98 99 # For HTTP server, try to get directory listing of /service/ 100 if [[ "$REPO_URL" == http://* ]]; then 101 local service_list_url="${REPO_URL}/service/" 102 log "Checking for services at: $service_list_url" 103 104 # Try to get directory listing (works with simple HTTP servers) 105 local services=() 106 if command -v curl &> /dev/null; then 107 # Extract directory names from HTTP directory listing 108 local listing 109 if listing=$(curl -fsSL "$service_list_url" 2>/dev/null); then 110 log "Directory listing received, parsing..." 111 log "Raw listing preview: $(echo "$listing" | head -10 | tr '\n' ' ')" 112 113 # Try multiple parsing approaches for different HTTP server formats 114 115 # Approach 1: Standard href links for directories 116 local href_services=($(echo "$listing" | grep -oE 'href="[^/"]+/"' | sed 's/href="//g' | sed 's/\/"//g' | grep -v '^\.$\|^\.\.$' | sort)) 117 log "Parsed href services: ${href_services[*]}" 118 119 # Approach 2: Simple directory names (for plain listings) 120 local plain_services=($(echo "$listing" | grep -oE '^[a-zA-Z][a-zA-Z0-9_-]*/$' | sed 's/\/$//g' | sort)) 121 log "Parsed plain services: ${plain_services[*]}" 122 123 # Combine and deduplicate results 124 services=() 125 if [[ ${#href_services[@]} -gt 0 ]] || [[ ${#plain_services[@]} -gt 0 ]]; then 126 services=($(printf '%s\n' "${href_services[@]}" "${plain_services[@]}" 2>/dev/null | sort -u)) 127 fi 128 129 log "Discovered services: ${services[*]}" 130 else 131 log "Failed to get directory listing from $service_list_url" 132 fi 133 fi 134 135 # If discovery failed, warn but don't fallback to hardcoded list 136 if [[ ${#services[@]} -eq 0 ]]; then 137 log "WARNING: Could not discover any local services" 138 log "This may be normal if no services are in development" 139 else 140 log "Final service list: ${services[*]}" 141 142 # Download each discovered service 143 for service in "${services[@]}"; do 144 download_service "$service" 145 done 146 fi 147 else 148 # For git repositories, we can't discover dynamically, so skip 149 log "Git repository detected - local services not available via git clone" 150 log "Local services are only available when using HTTP server during development" 151 fi 152} 153 154# Download a single service and all its files 155download_service() { 156 local service="$1" 157 log " Downloading $service service..." 158 159 # Create service directory in SERVICE_INSTALL_DIR (not INSTALL_DIR/service/) 160 mkdir -p "$SERVICE_INSTALL_DIR/$service" 161 162 # Try to download common service files 163 local service_files=( 164 "docker-compose.yml" 165 "setup.sh" 166 "README.md" 167 "Dockerfile" 168 "requirements.txt" 169 ) 170 171 local downloaded_count=0 172 for file in "${service_files[@]}"; do 173 local file_path="service/$service/$file" 174 local dest_path="$SERVICE_INSTALL_DIR/$service/$file" 175 176 # Try to download the file, but don't fail if it doesn't exist 177 if [[ "$REPO_URL" == http://* ]]; then 178 local url="${REPO_URL}/${file_path}" 179 else 180 local url="${REPO_URL}/raw/${BRANCH}/${file_path}" 181 fi 182 183 log " Downloading: $file from $url" 184 curl -v -L "$url" -o "$dest_path" --connect-timeout 10 --max-time 30 185 local curl_exit_code=$? 186 187 if [[ $curl_exit_code -eq 0 ]] && [[ -f "$dest_path" ]]; then 188 # Check if file is HTML (404 error page) and skip if so 189 if file "$dest_path" | grep -q "HTML" || head -1 "$dest_path" 2>/dev/null | grep -q "<!DOCTYPE"; then 190 log " ✗ Skipped: $file (got HTML error page, file doesn't exist)" 191 rm -f "$dest_path" 192 else 193 chmod +x "$dest_path" 2>/dev/null || true 194 downloaded_count=$((downloaded_count + 1)) 195 log " ✓ Downloaded: $file" 196 fi 197 else 198 log " ✗ Failed: $file (exit code: $curl_exit_code)" 199 fi 200 done 201 202 # Try to download app/ directory (common for Python services) 203 log " Checking for app/ directory..." 204 mkdir -p "$SERVICE_INSTALL_DIR/$service/app" 205 206 local app_files=("main.py" "__init__.py" "config.py" "routes.py") 207 local app_downloaded=false 208 for app_file in "${app_files[@]}"; do 209 local file_path="service/$service/app/$app_file" 210 local dest_path="$SERVICE_INSTALL_DIR/$service/app/$app_file" 211 212 if [[ "$REPO_URL" == http://* ]]; then 213 local url="${REPO_URL}/${file_path}" 214 else 215 local url="${REPO_URL}/raw/${BRANCH}/${file_path}" 216 fi 217 218 if curl -fsSL "$url" -o "$dest_path" --connect-timeout 10 --max-time 30 2>/dev/null; then 219 log " ✓ Downloaded: app/$app_file" 220 downloaded_count=$((downloaded_count + 1)) 221 app_downloaded=true 222 fi 223 done 224 225 # Clean up empty app directory if nothing was downloaded 226 if [[ "$app_downloaded" == false ]]; then 227 rmdir "$SERVICE_INSTALL_DIR/$service/app" 2>/dev/null || true 228 fi 229 230 231 if [[ $downloaded_count -gt 0 ]]; then 232 log "$service ($downloaded_count files)" 233 else 234 log "$service (no files found)" 235 # Remove empty directory 236 rmdir "$SERVICE_INSTALL_DIR/$service" 2>/dev/null || true 237 fi 238} 239 240# Bootstrap topsheet sheet infrastructure 241bootstrap_topsheet_sheet() { 242 log "" 243 log "Bootstrapping topsheet sheet infrastructure..." 244 245 # Check if topsheet.station-prod already exists 246 if [[ -d "/mnt/topsheet-station-prod" ]] || getent passwd topsheet-station-prod &>/dev/null; then 247 log "topsheet.station-prod already exists - skipping bootstrap" 248 return 249 fi 250 251 # First-time setup - bootstrap is required 252 log "" 253 log "REQUIRED: Setting up the topsheet (topsheet.station-prod)" 254 log "This creates essential infrastructure:" 255 log " - Global sheet registry (prevents UID collisions)" 256 log " - NAS credential storage" 257 log " - Service catalog management" 258 log "" 259 log "This is required for tinsnip to function properly." 260 log "" 261 262 # Check if we can read from terminal (not piped) 263 if [[ ! -t 0 ]]; then 264 log "ERROR: Interactive setup required but stdin is not a terminal (piped input detected)" 265 log "" 266 log "For first-time setup, run the installer interactively:" 267 log " curl -fsSL \"${REPO_URL}/raw/${BRANCH}/install.sh?\$(date +%s)\" -o /tmp/tinsnip-install.sh" 268 log " INSTALL_SERVICES=true bash /tmp/tinsnip-install.sh" 269 log "" 270 log "Or install CLI only and run bootstrap manually:" 271 log " export BOOTSTRAP_TOPSHEET=false" 272 log " curl -fsSL \"${REPO_URL}/raw/${BRANCH}/install.sh?\$(date +%s)\" | BOOTSTRAP_TOPSHEET=false INSTALL_SERVICES=true bash" 273 log " ~/.local/opt/dynamicalsystem.tinsnip/bin/tin machine station prod <your-nas-server>" 274 return 1 275 fi 276 277 # Check if topsheet NAS server is already registered 278 local nas_registry="/mnt/station-prod/data/nas-credentials/nas-servers" 279 if [[ -f "$nas_registry" ]] && grep -q "^topsheet=" "$nas_registry" 2>/dev/null; then 280 nas_server=$(grep "^topsheet=" "$nas_registry" | cut -d= -f2) 281 log "Using existing NAS server for topsheet sheet: $nas_server" 282 else 283 # Get NAS server for topsheet sheet 284 echo -e "\033[1;36mEnter NAS server hostname or IP for topsheet sheet: \033[0m\c" 285 read nas_server 286 if [[ -z "$nas_server" ]]; then 287 log "ERROR: NAS server is required to setop the topsheet" 288 log "You can complete setup later with:" 289 log " cd $INSTALL_DIR && TIN_SHEET=topsheet ./bin/tin machine create station prod <nas-server>" 290 return 1 291 fi 292 fi 293 294 setup_tinsnip="y" 295 case "$setup_tinsnip" in 296 [Yy]*) 297 log "Setting up topsheet sheet infrastructure..." 298 299 # Set up topsheet.station-prod 300 log "Creating topsheet.station-prod infrastructure..." 301 cd "$INSTALL_DIR" 302 303 if TIN_SHEET=topsheet ./bin/tin machine create station prod "$nas_server"; then 304 log "✅ topsheet.station-prod created successfully" 305 log "" 306 307 # Only offer SSH key generation if we don't already have working SSH 308 if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$nas_server" exit 2>/dev/null; then 309 log "" 310 log "Would you like to generate an SSH key for passwordless NAS access?" 311 log "This will enable automated operations without password prompts." 312 echo -e "\033[1;36mGenerate SSH key for $nas_server? [Y/n]: \033[0m\c" 313 read generate_key 314 else 315 log "✅ SSH access already configured for $nas_server" 316 generate_key="n" # Skip key generation 317 fi 318 case "${generate_key:-y}" in 319 [Nn]*) 320 log "Skipping SSH key generation" 321 log "You can generate it later with: tin key generate $nas_server" 322 ;; 323 *) 324 log "Generating SSH key for $nas_server..." 325 326 # Debug: Check if topsheet.station-prod infrastructure is ready 327 if [[ -d "/mnt/station-prod/data" ]]; then 328 log "✅ topsheet.station-prod infrastructure detected" 329 log " Mount point: /mnt/station-prod" 330 log " Data directory: /mnt/station-prod/data" 331 else 332 log "❌ topsheet.station-prod infrastructure not found" 333 log " Expected: /mnt/station-prod/data" 334 log " Mount check:" 335 mount | grep station-prod || echo " No station-prod mount found" 336 log " Directory check:" 337 ls -la /mnt/ | grep station || echo " No station directory in /mnt/" 338 fi 339 340 if "$INSTALL_DIR/bin/tin" key generate "$nas_server"; then 341 log "✅ SSH key generated and installed" 342 else 343 log "⚠️ SSH key generation failed - you can retry later" 344 log "Manual command: tin key generate $nas_server" 345 fi 346 ;; 347 esac 348 349 log "" 350 log "Platform infrastructure is ready:" 351 log " - Sheet registry: /mnt/station-prod/data/sheets" 352 log " - Service registry: /mnt/station-prod/data/services" 353 log " - NAS credentials: /mnt/station-prod/data/nas-credentials/" 354 log "" 355 log "You can now create other sheets:" 356 log " ./bin/tin machine create station prod $nas_server # (uses default sheet)" 357 log " TIN_SHEET=infrastructure ./bin/tin machine create station prod $nas_server" 358 else 359 log "WARNING: Failed to create topsheet.station-prod" 360 log "You can retry later with:" 361 log " cd $INSTALL_DIR && TIN_SHEET=topsheet ./bin/tin machine create station prod $nas_server" 362 fi 363 ;; 364 *) 365 log "Skipping topsheet setup" 366 log "You can set this up later with:" 367 log " cd $INSTALL_DIR && TIN_SHEET=topsheet ./bin/tin machine create station prod <nas-server>" 368 ;; 369 esac 370} 371 372main() { 373 log "tinsnip Infrastructure Installer (version: $INSTALLER_VERSION)" 374 log "==============================================================" 375 376 cd ~ || error "Failed to change to home directory" 377 378 if [[ ! -f /etc/os-release ]] || ! grep -q "Ubuntu" /etc/os-release; then 379 error "This installer requires Ubuntu" 380 fi 381 382 if [[ -d "$INSTALL_DIR" ]]; then 383 log "Tinsnip directory already exists. Removing for fresh installation..." 384 log " Removing: $INSTALL_DIR" 385 rm -rf "$INSTALL_DIR" 386 fi 387 388 log " Creating: $INSTALL_DIR" 389 mkdir -p "$INSTALL_DIR"/{scripts,machine/scripts,config} 390 391 log "Downloading setup files..." 392 393 # Download main files 394 download_file "setup.sh" "$INSTALL_DIR/setup.sh" 395 download_file "README.md" "$INSTALL_DIR/README.md" 396 397 # Download CLI commands 398 mkdir -p "$INSTALL_DIR/bin" 399 download_file "bin/tin" "$INSTALL_DIR/bin/tin" 400 # All CLI functionality now migrated to cmd/ structure 401 402 # Legacy deployment scripts removed - functionality moved to CLI 403 404 # Download machine infrastructure (the important stuff!) 405 download_file "machine/teardown.sh" "$INSTALL_DIR/machine/teardown.sh" 406 407 # Download new library structure 408 mkdir -p "$INSTALL_DIR/lib" 409 download_file "lib/core.sh" "$INSTALL_DIR/lib/core.sh" 410 download_file "lib/uid.sh" "$INSTALL_DIR/lib/uid.sh" 411 download_file "lib/registry.sh" "$INSTALL_DIR/lib/registry.sh" 412 download_file "lib/nas.sh" "$INSTALL_DIR/lib/nas.sh" 413 download_file "lib/nfs.sh" "$INSTALL_DIR/lib/nfs.sh" 414 download_file "lib/docker.sh" "$INSTALL_DIR/lib/docker.sh" 415 download_file "lib/metadata.sh" "$INSTALL_DIR/lib/metadata.sh" 416 417 # Download validation scripts 418 mkdir -p "$INSTALL_DIR/validation" 419 download_file "validation/validate.sh" "$INSTALL_DIR/validation/validate.sh" 420 download_file "validation/validate_functions.sh" "$INSTALL_DIR/validation/validate_functions.sh" 421 download_file "validation/validate_dry_run.sh" "$INSTALL_DIR/validation/validate_dry_run.sh" 422 download_file "validation/validate_port_edge_cases.sh" "$INSTALL_DIR/validation/validate_port_edge_cases.sh" 423 download_file "validation/validate_docker.sh" "$INSTALL_DIR/validation/validate_docker.sh" 424 425 # Download remaining machine scripts (still needed for now) 426 download_file "machine/setup_service.sh" "$INSTALL_DIR/machine/setup_service.sh" 427 download_file "machine/setup_station.sh" "$INSTALL_DIR/machine/setup_station.sh" 428 download_file "machine/install_docker.sh" "$INSTALL_DIR/machine/install_docker.sh" 429 430 # Legacy lib.sh removed - functionality consolidated into ./lib/ modules 431 432 # Note: Service catalog is maintained in separate repository 433 # Installed via install_service_catalog() to ~/.local/opt/{sheet}.service/ 434 435 # Download new CMD structure 436 mkdir -p "$INSTALL_DIR/cmd/"{sheet,key,machine,service,nas,registry,status} 437 438 # Sheet commands 439 download_file "cmd/sheet/create.sh" "$INSTALL_DIR/cmd/sheet/create.sh" 440 download_file "cmd/sheet/list.sh" "$INSTALL_DIR/cmd/sheet/list.sh" 441 download_file "cmd/sheet/show.sh" "$INSTALL_DIR/cmd/sheet/show.sh" 442 download_file "cmd/sheet/rm.sh" "$INSTALL_DIR/cmd/sheet/rm.sh" 443 444 # Key commands 445 download_file "cmd/key/generate.sh" "$INSTALL_DIR/cmd/key/generate.sh" 446 download_file "cmd/key/install.sh" "$INSTALL_DIR/cmd/key/install.sh" 447 download_file "cmd/key/list.sh" "$INSTALL_DIR/cmd/key/list.sh" 448 download_file "cmd/key/remove.sh" "$INSTALL_DIR/cmd/key/remove.sh" 449 download_file "cmd/key/status.sh" "$INSTALL_DIR/cmd/key/status.sh" 450 download_file "cmd/key/show.sh" "$INSTALL_DIR/cmd/key/show.sh" 451 download_file "cmd/key/test.sh" "$INSTALL_DIR/cmd/key/test.sh" 452 453 # Machine commands 454 download_file "cmd/machine/create.sh" "$INSTALL_DIR/cmd/machine/create.sh" 455 download_file "cmd/machine/list.sh" "$INSTALL_DIR/cmd/machine/list.sh" 456 download_file "cmd/machine/status.sh" "$INSTALL_DIR/cmd/machine/status.sh" 457 download_file "cmd/machine/rm.sh" "$INSTALL_DIR/cmd/machine/rm.sh" 458 459 # NAS commands 460 download_file "cmd/nas/list.sh" "$INSTALL_DIR/cmd/nas/list.sh" 461 download_file "cmd/nas/show.sh" "$INSTALL_DIR/cmd/nas/show.sh" 462 download_file "cmd/nas/add.sh" "$INSTALL_DIR/cmd/nas/add.sh" 463 download_file "cmd/nas/discover.sh" "$INSTALL_DIR/cmd/nas/discover.sh" 464 465 # Service commands 466 download_file "cmd/service/deploy.sh" "$INSTALL_DIR/cmd/service/deploy.sh" 467 download_file "cmd/service/list.sh" "$INSTALL_DIR/cmd/service/list.sh" 468 download_file "cmd/service/status.sh" "$INSTALL_DIR/cmd/service/status.sh" 469 download_file "cmd/service/logs.sh" "$INSTALL_DIR/cmd/service/logs.sh" 470 download_file "cmd/service/stop.sh" "$INSTALL_DIR/cmd/service/stop.sh" 471 download_file "cmd/service/rm.sh" "$INSTALL_DIR/cmd/service/rm.sh" 472 473 # Registry and Status commands 474 download_file "cmd/registry/show.sh" "$INSTALL_DIR/cmd/registry/show.sh" 475 download_file "cmd/status/show.sh" "$INSTALL_DIR/cmd/status/show.sh" 476 477 # Station service scripts removed - functionality migrated to tin CLI 478 479 # Always install service catalog first (git clone if available) 480 install_service_catalog 481 482 # Download all local services from ./service/ directory (adds to catalog) 483 download_local_services 484 485 # Copy all local services to service catalog 486 if [[ -d "$INSTALL_DIR/service" ]]; then 487 log "Installing local services to catalog..." 488 mkdir -p "$SERVICE_INSTALL_DIR" 489 490 # Copy all services found in INSTALL_DIR/service/ 491 local service_count=0 492 for service_dir in "$INSTALL_DIR/service"/*; do 493 if [[ -d "$service_dir" ]]; then 494 local service_name=$(basename "$service_dir") 495 # Skip station service (it's infrastructure, not a containerised service) 496 if [[ "$service_name" != "station" ]]; then 497 cp -r "$service_dir" "$SERVICE_INSTALL_DIR/" 498 chmod +x "$SERVICE_INSTALL_DIR/$service_name/setup.sh" 2>/dev/null || true 499 log "$service_name" 500 service_count=$((service_count + 1)) 501 fi 502 fi 503 done 504 505 if [[ $service_count -eq 0 ]]; then 506 log " No local services found to install" 507 else 508 log " Installed $service_count local service(s)" 509 fi 510 fi 511 512 log "Installation complete!" 513 log "" 514 log "Adding tinsnip CLI to your PATH..." 515 516 # Add to PATH in shell profiles if not already present 517 local path_export="export PATH=\"$INSTALL_DIR/bin:\$PATH\"" 518 local added_to_profile=false 519 520 # Add to ~/.bashrc if it exists and path not already there 521 if [[ -f ~/.bashrc ]] && ! grep -q "$INSTALL_DIR/bin" ~/.bashrc 2>/dev/null; then 522 echo "" >> ~/.bashrc 523 echo "# tinsnip CLI" >> ~/.bashrc 524 echo "$path_export" >> ~/.bashrc 525 log " Added to ~/.bashrc" 526 added_to_profile=true 527 fi 528 529 # Add to ~/.bash_profile if it exists and path not already there 530 if [[ -f ~/.bash_profile ]] && ! grep -q "$INSTALL_DIR/bin" ~/.bash_profile 2>/dev/null; then 531 echo "" >> ~/.bash_profile 532 echo "# tinsnip CLI" >> ~/.bash_profile 533 echo "$path_export" >> ~/.bash_profile 534 log " Added to ~/.bash_profile" 535 added_to_profile=true 536 fi 537 538 # Add to ~/.profile as fallback if no other profile was updated 539 if [[ "$added_to_profile" == false ]]; then 540 if [[ ! -f ~/.profile ]] || ! grep -q "$INSTALL_DIR/bin" ~/.profile 2>/dev/null; then 541 echo "" >> ~/.profile 542 echo "# tinsnip CLI" >> ~/.profile 543 echo "$path_export" >> ~/.profile 544 log " Added to ~/.profile" 545 added_to_profile=true 546 fi 547 fi 548 549 if [[ "$added_to_profile" == true ]]; then 550 log " ✅ PATH updated in shell profile(s)" 551 log " ⚠️ Start a new shell session or run: source ~/.bashrc" 552 else 553 log " ⚠️ PATH already configured or no shell profile found" 554 fi 555 556 log "" 557 log "Verify installation:" 558 log " $INSTALL_DIR/bin/tin --version" 559 log " $INSTALL_DIR/bin/tin help" 560 log "" 561 562 # Bootstrap topsheet if requested (after CLI is installed and in PATH) 563 if [[ "${BOOTSTRAP_TOPSHEET:-true}" == "true" ]]; then 564 bootstrap_topsheet_sheet 565 fi 566 567 log "" 568 log "Next steps:" 569 log "1. Set up topsheet (one-time):" 570 log " TIN_SHEET=topsheet $INSTALL_DIR/bin/tin machine station prod <nas-server>" 571 log "" 572 log "2. Create a sheet and deploy services:" 573 log " $INSTALL_DIR/bin/tin sheet infrastructure" 574 log " $INSTALL_DIR/bin/tin machine gateway prod" 575 log " $INSTALL_DIR/bin/tin service gateway-prod lldap" 576 log "" 577 log "3. Deploy services:" 578 log " $INSTALL_DIR/bin/tin machine marshal prod # External route coordinator" 579 log " $INSTALL_DIR/bin/tin service marshal-prod marshal" 580 log " $INSTALL_DIR/bin/tin machine pds prod # atproto Personal Data Server" 581 log " $INSTALL_DIR/bin/tin service pds-prod pds" 582 log "" 583 log "Or use the legacy script interface:" 584 if [[ -d "$SERVICE_INSTALL_DIR" ]]; then 585 # Get list of available services from the catalog 586 for service in $(ls -1 "$SERVICE_INSTALL_DIR" 2>/dev/null | grep -v README | head -3); do 587 log " cd $INSTALL_DIR && ./bin/tin machine create $service prod DS412plus.local" 588 done 589 else 590 log " cd $INSTALL_DIR && ./bin/tin machine create gateway prod DS412plus.local" 591 fi 592} 593 594main "$@"