-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocs.html
More file actions
749 lines (651 loc) · 43.6 KB
/
docs.html
File metadata and controls
749 lines (651 loc) · 43.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Docs — Pabawi</title>
<meta name="description" content="Pabawi documentation: quick start, configuration, integrations, API reference, and deployment guides." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/style.css" />
<link rel="icon" href="favicon.svg" type="image/svg+xml" />
</head>
<body>
<!-- NAV -->
<nav class="nav">
<div class="nav-inner">
<a href="index.html" class="nav-logo">
<svg width="28" height="28" viewBox="0 0 28 28" fill="none">
<rect width="28" height="28" rx="7" fill="url(#lgd)"/>
<path d="M7 14l5 5 9-9" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<defs><linearGradient id="lgd" x1="0" y1="0" x2="28" y2="28" gradientUnits="userSpaceOnUse"><stop stop-color="#6366f1"/><stop offset="1" stop-color="#22d3ee"/></linearGradient></defs>
</svg>
Pabawi
</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="docs.html" class="active">Docs</a></li>
<li><a href="about.html">About</a></li>
<li><a href="https://github.com/example42/pabawi" target="_blank" rel="noopener">GitHub ↗</a></li>
<li><a href="https://github.com/example42/pabawi/releases" target="_blank" rel="noopener" class="nav-cta">Get Started</a></li>
</ul>
<button class="nav-hamburger" aria-label="Menu">
<svg width="22" height="22" fill="none" viewBox="0 0 22 22"><path d="M3 6h16M3 11h16M3 16h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
</button>
</div>
</nav>
<!-- DOCS LAYOUT -->
<div class="docs-layout">
<!-- SIDEBAR -->
<aside class="docs-sidebar">
<h4>Getting Started</h4>
<ul>
<li><a href="#quickstart" class="active">Quick Start</a></li>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#manual-setup">Manual Setup</a></li>
<li><a href="#docker">Docker Deployment</a></li>
</ul>
<h4>Configuration</h4>
<ul>
<li><a href="#configuration">Overview</a></li>
<li><a href="#core-config">Core Settings</a></li>
<li><a href="#auth-config">Authentication</a></li>
</ul>
<h4>Integrations</h4>
<ul>
<li><a href="#integrations">Overview</a></li>
<li><a href="#bolt">Bolt</a></li>
<li><a href="#puppetdb">PuppetDB</a></li>
<li><a href="#puppetserver">Puppetserver</a></li>
<li><a href="#hiera">Hiera</a></li>
<li><a href="#ansible">Ansible</a></li>
<li><a href="#ssh">SSH</a></li>
<li><a href="#proxmox">Proxmox</a></li>
<li><a href="#aws">AWS EC2</a></li>
</ul>
<h4>Using Pabawi</h4>
<ul>
<li><a href="#inventory">Inventory</a></li>
<li><a href="#execution">Command Execution</a></li>
<li><a href="#lifecycle">VM Lifecycle</a></li>
<li><a href="#history">Execution History</a></li>
<li><a href="#node-journal">Node Journal</a></li>
<li><a href="#rbac">RBAC & Permissions</a></li>
<li><a href="#status-dashboard">Status Dashboard</a></li>
<li><a href="#setup-wizards">Setup Wizards</a></li>
<li><a href="#expert-mode">Expert Mode</a></li>
</ul>
<h4>Reference</h4>
<ul>
<li><a href="#api">API Reference</a></li>
<li><a href="#env-vars">Environment Variables</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ul>
</aside>
<!-- MAIN CONTENT -->
<main class="docs-content">
<h1>Documentation</h1>
<p class="doc-subtitle">Everything you need to install, configure, and use Pabawi to manage your infrastructure.</p>
<!-- ===== QUICK START ===== -->
<div class="docs-section" id="quickstart">
<h2>Quick Start</h2>
<p>The fastest way to get Pabawi running is with the interactive setup script. It handles prerequisite checks, config file generation, dependency installation, and first launch.</p>
<div class="docs-code">
<div class="code-header" style="display:flex;justify-content:space-between;padding:.5rem 1rem;background:rgba(31,41,55,.6);border-bottom:1px solid var(--border);">
<span class="code-lang">bash</span>
<button class="copy-btn">Copy</button>
</div>
<pre>
<span class="c-prompt">$ </span><span class="c-cmd">git clone https://github.com/example42/pabawi</span>
<span class="c-prompt">$ </span><span class="c-cmd">cd pabawi</span>
<span class="c-prompt">$ </span><span class="c-cmd">./scripts/setup.sh</span></pre>
</div>
<p>The setup wizard will:</p>
<ol style="padding-left:1.5rem;color:var(--text-muted);font-size:.95rem;line-height:2;margin-bottom:1rem">
<li>Check for Node.js 20+, npm 9+, and optionally Bolt, Ansible, Puppet CLIs</li>
<li>Generate <code class="inline">backend/.env</code> with smart defaults based on detected tools and SSL certs</li>
<li>Run <code class="inline">npm run install:all</code></li>
<li>Ask whether to start in dev mode, full-stack mode, or exit</li>
</ol>
<p>Once running, open <code class="inline">http://localhost:3000</code> in your browser.</p>
<div class="docs-callout tip">
<p><strong>Tip:</strong> You don't need Puppet, Bolt, or Ansible to get started. Enable SSH integration only and Pabawi works as a standalone web UI for your servers.</p>
</div>
</div>
<!-- ===== PREREQUISITES ===== -->
<div class="docs-section" id="prerequisites">
<h2>Prerequisites</h2>
<p>Hard requirements (always needed):</p>
<ul>
<li><strong>Node.js 20+</strong> and <strong>npm 9+</strong> — or use the Docker image which bundles everything</li>
</ul>
<p>Soft requirements (only needed for specific integrations):</p>
<ul>
<li><strong>Bolt CLI</strong> — for Bolt integration (task/command execution via Bolt)</li>
<li><strong>Ansible CLI</strong> — for Ansible integration</li>
<li><strong>Puppet/OpenVox agent</strong> — provides SSL certs for PuppetDB and Puppetserver integrations</li>
<li><strong>Control repo</strong> — for Hiera integration (local path to your Puppet control repository)</li>
<li><strong>SSH access</strong> — configured SSH keys/config for SSH integration</li>
</ul>
</div>
<!-- ===== MANUAL SETUP ===== -->
<div class="docs-section" id="manual-setup">
<h2>Manual Setup</h2>
<p>If you prefer to configure things by hand:</p>
<div class="docs-code">
<div class="code-header" style="display:flex;justify-content:space-between;padding:.5rem 1rem;background:rgba(31,41,55,.6);border-bottom:1px solid var(--border);">
<span class="code-lang">bash</span><button class="copy-btn">Copy</button>
</div>
<pre>
<span class="c-prompt">$ </span><span class="c-cmd">git clone https://github.com/example42/pabawi && cd pabawi</span>
<span class="c-prompt">$ </span><span class="c-cmd">npm run install:all</span>
<span class="c-prompt">$ </span><span class="c-cmd">cp backend/.env.example backend/.env</span>
<span class="c-cmt"># Edit backend/.env with your settings</span>
<span class="c-cmt"># Development mode (two terminals):</span>
<span class="c-prompt">$ </span><span class="c-cmd">npm run dev:backend </span><span class="c-cmt"># port 3000</span>
<span class="c-prompt">$ </span><span class="c-cmd">npm run dev:frontend </span><span class="c-cmt"># port 5173, proxies API to 3000</span>
<span class="c-cmt"># Or build frontend and serve everything from backend:</span>
<span class="c-prompt">$ </span><span class="c-cmd">npm run dev:fullstack </span><span class="c-cmt"># port 3000</span></pre>
</div>
</div>
<!-- ===== DOCKER ===== -->
<div class="docs-section" id="docker">
<h2>Docker Deployment</h2>
<p>Pabawi ships as a single container image (<code class="inline">example42/pabawi</code>) supporting amd64 and arm64.</p>
<div class="docs-code">
<div class="code-header" style="display:flex;justify-content:space-between;padding:.5rem 1rem;background:rgba(31,41,55,.6);border-bottom:1px solid var(--border);">
<span class="code-lang">bash</span><button class="copy-btn">Copy</button>
</div>
<pre>
<span class="c-cmt"># Create a directory to hold all Pabawi data</span>
<span class="c-prompt">$ </span><span class="c-cmd">mkdir pabawi && cd pabawi</span>
<span class="c-cmt"># Create your .env (see environment variables section)</span>
<span class="c-prompt">$ </span><span class="c-cmd">vi .env</span>
<span class="c-cmt"># Run the container</span>
<span class="c-prompt">$ </span><span class="c-cmd">docker run -d \</span>
--name pabawi \
--user "$(id -u):1001" \
-p 127.0.0.1:3000:3000 \
-v "$(pwd):/pabawi" \
--env-file ".env" \
example42/pabawi:latest</pre>
</div>
<p>Mount points inside the container:</p>
<table class="docs-table">
<thead><tr><th>Path</th><th>Purpose</th></tr></thead>
<tbody>
<tr><td>/pabawi/data</td><td>SQLite database storage</td></tr>
<tr><td>/pabawi/certs</td><td>SSL certs for PuppetDB/Puppetserver</td></tr>
<tr><td>/pabawi/control-repo</td><td>Puppet control repo for Hiera</td></tr>
<tr><td>/pabawi/bolt-project</td><td>Bolt project directory</td></tr>
</tbody>
</table>
<div class="docs-callout warn">
<p><strong>Note:</strong> All paths in <code class="inline">.env</code> are relative to the container's filesystem, not your host.</p>
</div>
</div>
<!-- ===== CONFIGURATION ===== -->
<div class="docs-section" id="configuration">
<h2>Configuration</h2>
<p>All configuration lives in <code class="inline">backend/.env</code>. Use <code class="inline">backend/.env.example</code> as a reference. Never commit <code class="inline">.env</code> to version control.</p>
<p>The <code class="inline">ConfigService</code> wraps all environment variables with Zod validation — missing required vars cause a clear startup error rather than a silent runtime failure.</p>
</div>
<div class="docs-section" id="core-config">
<h2>Core Settings</h2>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Default</th><th>Description</th></tr></thead>
<tbody>
<tr><td>PORT</td><td>3000</td><td>HTTP server port</td></tr>
<tr><td>HOST</td><td>0.0.0.0</td><td>Bind address</td></tr>
<tr><td>LOG_LEVEL</td><td>info</td><td>debug / info / warn / error</td></tr>
<tr><td>DATABASE_PATH</td><td>./data/pabawi.db</td><td>SQLite database file path</td></tr>
<tr><td>CONCURRENT_EXECUTION_LIMIT</td><td>5</td><td>Max parallel command executions</td></tr>
<tr><td>COMMAND_WHITELIST_ENABLED</td><td>true</td><td>Enable/disable command whitelist</td></tr>
<tr><td>COMMAND_WHITELIST</td><td>(see below)</td><td>Comma-separated list of allowed commands</td></tr>
<tr><td>CACHE_INVENTORY_TTL</td><td>30</td><td>Inventory cache TTL (seconds)</td></tr>
<tr><td>CACHE_FACTS_TTL</td><td>300</td><td>Facts cache TTL (seconds)</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="auth-config">
<h2>Authentication</h2>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Default</th><th>Description</th></tr></thead>
<tbody>
<tr><td>AUTH_ENABLED</td><td>true</td><td>Enable JWT authentication</td></tr>
<tr><td>JWT_SECRET</td><td>—</td><td><strong>Required.</strong> Secret key for JWT signing (min 32 chars)</td></tr>
<tr><td>JWT_EXPIRY</td><td>24h</td><td>Token expiry duration</td></tr>
<tr><td>DEFAULT_ADMIN_USERNAME</td><td>admin</td><td>Initial admin account username</td></tr>
<tr><td>DEFAULT_ADMIN_PASSWORD</td><td>—</td><td>Initial admin password (change on first login)</td></tr>
</tbody>
</table>
<div class="docs-callout warn">
<p><strong>Security:</strong> Always set a strong <code class="inline">JWT_SECRET</code>. Run <code class="inline">openssl rand -hex 32</code> to generate one. Never reuse this value across environments.</p>
</div>
</div>
<!-- ===== INTEGRATIONS ===== -->
<div class="docs-section" id="integrations">
<h2>Integrations Overview</h2>
<p>Pabawi uses a plugin architecture. Each integration is independent — disable what you don't need, and the rest continue working. The <code class="inline">IntegrationManager</code> fans out inventory and fact requests to all enabled plugins and merges results using a priority system.</p>
<table class="docs-table">
<thead><tr><th>Plugin</th><th>Priority</th><th>Execution</th><th>Inventory</th><th>Provisioning</th></tr></thead>
<tbody>
<tr><td>SSH</td><td>50 (highest)</td><td>✓</td><td>✓</td><td>—</td></tr>
<tr><td>Puppetserver</td><td>20</td><td>—</td><td>✓</td><td>—</td></tr>
<tr><td>Bolt</td><td>10</td><td>✓</td><td>✓</td><td>—</td></tr>
<tr><td>PuppetDB</td><td>10</td><td>—</td><td>✓</td><td>—</td></tr>
<tr><td>Ansible</td><td>8</td><td>✓</td><td>✓</td><td>—</td></tr>
<tr><td>Hiera</td><td>6</td><td>—</td><td>—</td><td>—</td></tr>
<tr><td>Proxmox</td><td>—</td><td>—</td><td>✓</td><td>✓ VMs & LXC</td></tr>
<tr><td>AWS</td><td>—</td><td>—</td><td>✓</td><td>✓ EC2</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="bolt">
<h2>Bolt Integration</h2>
<p>Bolt provides command execution, task execution, and inventory discovery. Pabawi spawns the Bolt CLI directly — your existing Bolt project and inventory are used as-is.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>BOLT_ENABLED</td><td>true / false</td></tr>
<tr><td>BOLT_PROJECT_PATH</td><td>Absolute path to your Bolt project directory</td></tr>
<tr><td>BOLT_COMMAND_TIMEOUT</td><td>Execution timeout in seconds (default: 120)</td></tr>
<tr><td>BOLT_INVENTORY_FILE</td><td>Path to inventory.yaml (default: auto-detected)</td></tr>
</tbody>
</table>
<div class="docs-callout">
<p><strong>Note:</strong> The Bolt user must have read access to the project directory and network access to target nodes. Bolt tasks are automatically discovered from the project's <code class="inline">modules/</code> directory.</p>
</div>
</div>
<div class="docs-section" id="puppetdb">
<h2>PuppetDB Integration</h2>
<p>PuppetDB provides rich inventory, node facts, Puppet run reports, and event data. Supports both Puppet Enterprise and Open Source Puppet / OpenVox.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>PUPPETDB_ENABLED</td><td>true / false</td></tr>
<tr><td>PUPPETDB_URL</td><td>PuppetDB URL (e.g. <code class="inline">https://puppet.example.com:8081</code>)</td></tr>
<tr><td>PUPPETDB_SSL_CERT</td><td>Path to client SSL certificate (.pem)</td></tr>
<tr><td>PUPPETDB_SSL_KEY</td><td>Path to client SSL private key (.pem)</td></tr>
<tr><td>PUPPETDB_SSL_CA</td><td>Path to CA certificate</td></tr>
<tr><td>PUPPETDB_TOKEN</td><td>PE RBAC token (alternative to SSL certs)</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="puppetserver">
<h2>Puppetserver Integration</h2>
<p>Puppetserver integration provides node certificate listing, server status, and catalog compilation.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>PUPPETSERVER_ENABLED</td><td>true / false</td></tr>
<tr><td>PUPPETSERVER_URL</td><td>Puppetserver URL (e.g. <code class="inline">https://puppet.example.com:8140</code>)</td></tr>
<tr><td>PUPPETSERVER_SSL_CERT</td><td>Path to client certificate</td></tr>
<tr><td>PUPPETSERVER_SSL_KEY</td><td>Path to client private key</td></tr>
<tr><td>PUPPETSERVER_SSL_CA</td><td>Path to CA certificate</td></tr>
<tr><td>PUPPETSERVER_ENVIRONMENT</td><td>Default environment (default: production)</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="hiera">
<h2>Hiera Integration</h2>
<p>Browse and resolve Hiera data hierarchically, with fact-based interpolation and class-aware key analysis. Requires a local copy of your Puppet control repository.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>HIERA_ENABLED</td><td>true / false</td></tr>
<tr><td>HIERA_CONTROL_REPO_PATH</td><td>Absolute path to your control repo</td></tr>
<tr><td>HIERA_ENVIRONMENTS</td><td>Comma-separated environment list (default: production)</td></tr>
<tr><td>HIERA_DEFAULT_ENVIRONMENT</td><td>Default environment for lookups</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="ansible">
<h2>Ansible Integration</h2>
<p>Run ad-hoc commands, execute playbooks, and discover inventory via Ansible. Pabawi spawns the Ansible CLI using your existing project.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>ANSIBLE_ENABLED</td><td>true / false</td></tr>
<tr><td>ANSIBLE_PROJECT_PATH</td><td>Absolute path to your Ansible project</td></tr>
<tr><td>ANSIBLE_INVENTORY_PATH</td><td>Path to inventory file or directory</td></tr>
<tr><td>ANSIBLE_COMMAND_TIMEOUT</td><td>Execution timeout in seconds (default: 120)</td></tr>
<tr><td>ANSIBLE_VAULT_PASSWORD_FILE</td><td>Path to vault password file (optional)</td></tr>
</tbody>
</table>
</div>
<div class="docs-section" id="ssh">
<h2>SSH Integration</h2>
<p>Direct SSH command execution — the simplest integration. Works standalone without any other tools. Ideal for homelabs or legacy nodes outside Puppet/Ansible.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>SSH_ENABLED</td><td>true / false</td></tr>
<tr><td>SSH_CONFIG_PATH</td><td>Path to SSH config file (default: ~/.ssh/config)</td></tr>
<tr><td>SSH_DEFAULT_USER</td><td>Default SSH user for connections</td></tr>
<tr><td>SSH_DEFAULT_KEY_PATH</td><td>Default private key path</td></tr>
<tr><td>SSH_USE_SUDO</td><td>Prepend sudo to commands (default: false)</td></tr>
<tr><td>SSH_CONNECTION_TIMEOUT</td><td>Connection timeout in seconds (default: 10)</td></tr>
<tr><td>SSH_MAX_CONNECTIONS</td><td>Connection pool size (default: 10)</td></tr>
<tr><td>SSH_INVENTORY</td><td>Static host list (JSON array of host objects)</td></tr>
</tbody>
</table>
<div class="docs-callout tip">
<p><strong>Tip:</strong> Set <code class="inline">SSH_INVENTORY</code> to a JSON array to define hosts when no other inventory source is configured: <code class="inline">SSH_INVENTORY=[{"name":"web01","host":"192.168.1.10"}]</code></p>
</div>
</div>
<div class="docs-section" id="proxmox">
<h2>Proxmox Integration <span style="font-size:.72rem;background:rgba(251,146,60,.15);border:1px solid rgba(251,146,60,.35);color:#fb923c;padding:.15rem .55rem;border-radius:100px;vertical-align:middle;font-weight:700;letter-spacing:.04em">v0.9+</span></h2>
<p>Proxmox integration provides VM and LXC container inventory discovery, lifecycle management (start/stop/reboot/destroy), and provisioning — all alongside your existing config management tools.</p>
<h3>Authentication</h3>
<p>Token-based authentication is recommended. Create a dedicated API token in the Proxmox UI under <strong>Datacenter → Permissions → API Tokens</strong> with appropriate privileges.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>PROXMOX_ENABLED</td><td>true / false</td></tr>
<tr><td>PROXMOX_HOST</td><td>Proxmox host (e.g. <code class="inline">proxmox.example.com</code>)</td></tr>
<tr><td>PROXMOX_PORT</td><td>API port (default: 8006)</td></tr>
<tr><td>PROXMOX_TOKEN</td><td>API token: <code class="inline">user@realm!tokenid=uuid</code> (recommended)</td></tr>
<tr><td>PROXMOX_USERNAME</td><td>Username for password auth (alternative to token)</td></tr>
<tr><td>PROXMOX_PASSWORD</td><td>Password for password auth</td></tr>
<tr><td>PROXMOX_REALM</td><td>Auth realm (default: pam)</td></tr>
<tr><td>PROXMOX_SSL_VERIFY</td><td>Verify SSL certificate (default: true)</td></tr>
<tr><td>PROXMOX_TIMEOUT</td><td>API timeout in ms (default: 30000)</td></tr>
</tbody>
</table>
<h3>Capabilities</h3>
<ul>
<li><strong>Inventory</strong> — auto-discover all VMs and containers; group by node, status (running/stopped), or type (QEMU/LXC)</li>
<li><strong>Lifecycle</strong> — start, stop, shutdown, reboot, suspend (VMs), resume, destroy — with confirmation dialogs for destructive actions</li>
<li><strong>Provisioning</strong> — create new VMs and LXC containers with full parameter control via the Manage tab</li>
<li><strong>Facts</strong> — CPU, memory, disk, network config, status, uptime</li>
</ul>
<p>Node identifiers follow the format <code class="inline">proxmox:<node>:<vmid></code> (e.g. <code class="inline">proxmox:pve1:101</code>).</p>
<div class="docs-callout warn">
<p><strong>IAM note:</strong> The Proxmox API token must have at minimum <code class="inline">VM.PowerMgmt</code>, <code class="inline">VM.Audit</code>, and <code class="inline">VM.Allocate</code> privileges on the relevant resource pools.</p>
</div>
</div>
<div class="docs-section" id="aws">
<h2>AWS EC2 Integration <span style="font-size:.72rem;background:rgba(245,158,11,.15);border:1px solid rgba(245,158,11,.35);color:#f59e0b;padding:.15rem .55rem;border-radius:100px;vertical-align:middle;font-weight:700;letter-spacing:.04em">v0.10+</span></h2>
<p>AWS integration discovers EC2 instances across one or more regions, manages their lifecycle, and provisions new instances — making cloud VMs first-class citizens alongside your on-prem fleet.</p>
<table class="docs-table">
<thead><tr><th>Variable</th><th>Description</th></tr></thead>
<tbody>
<tr><td>AWS_ENABLED</td><td>true / false</td></tr>
<tr><td>AWS_ACCESS_KEY_ID</td><td>IAM access key ID</td></tr>
<tr><td>AWS_SECRET_ACCESS_KEY</td><td>IAM secret access key</td></tr>
<tr><td>AWS_DEFAULT_REGION</td><td>Default region (e.g. <code class="inline">us-east-1</code>)</td></tr>
<tr><td>AWS_REGIONS</td><td>JSON array of regions to discover (e.g. <code class="inline">["us-east-1","eu-west-1"]</code>)</td></tr>
<tr><td>AWS_PROFILE</td><td>AWS CLI profile name (alternative to access keys)</td></tr>
<tr><td>AWS_SESSION_TOKEN</td><td>STS session token for temporary credentials</td></tr>
</tbody>
</table>
<h3>Required IAM Permissions</h3>
<div class="docs-code">
<pre>
<span class="c-cmt"># Minimum IAM permissions required</span>
ec2:DescribeInstances, DescribeRegions, DescribeInstanceTypes
ec2:DescribeImages, DescribeVpcs, DescribeSubnets
ec2:DescribeSecurityGroups, DescribeKeyPairs
ec2:RunInstances, StartInstances, StopInstances
ec2:RebootInstances, TerminateInstances
sts:GetCallerIdentity</pre>
</div>
<h3>Capabilities</h3>
<ul>
<li><strong>Inventory</strong> — discover EC2 instances across all configured regions; group by region, VPC, or tags</li>
<li><strong>Lifecycle</strong> — start, stop, reboot, terminate instances from the Manage tab</li>
<li><strong>Provisioning</strong> — launch new EC2 instances (AMI, instance type, VPC, subnet, security groups, key pair)</li>
<li><strong>Health check</strong> — validates credentials via <code class="inline">sts:GetCallerIdentity</code> on startup</li>
</ul>
<p>Node identifiers follow the format <code class="inline">aws:<region>:<instance-id></code> (e.g. <code class="inline">aws:us-east-1:i-0abc1234</code>).</p>
<div class="docs-callout tip">
<p><strong>Tip:</strong> If running Pabawi on an EC2 instance, you can omit <code class="inline">AWS_ACCESS_KEY_ID</code> and <code class="inline">AWS_SECRET_ACCESS_KEY</code> and attach an IAM instance profile instead — the default AWS credential chain is used.</p>
</div>
</div>
<!-- ===== USING PABAWI ===== -->
<div class="docs-section" id="inventory">
<h2>Inventory</h2>
<p>The Inventory page aggregates nodes from all enabled integrations. Each node shows its source integration (colour-coded), transport type, and connection address.</p>
<ul>
<li><strong>Search</strong> — type to filter by node name (300ms debounce)</li>
<li><strong>Filter by integration</strong> — show nodes from a single source</li>
<li><strong>Virtual scrolling</strong> — handles thousands of nodes without performance issues</li>
<li><strong>Groups</strong> — Bolt inventory groups and Ansible groups are preserved and browsable</li>
<li><strong>Node detail</strong> — click any node to see facts, classes, recent reports, and execution history</li>
</ul>
</div>
<div class="docs-section" id="execution">
<h2>Command Execution</h2>
<p>Select one or more target nodes, choose an integration (Bolt, Ansible, or SSH), enter your command or select a task, and execute. Output streams in real-time via SSE.</p>
<h3>Command Whitelist</h3>
<p>When <code class="inline">COMMAND_WHITELIST_ENABLED=true</code>, only commands matching the whitelist may be executed. The whitelist supports exact matches and glob patterns:</p>
<div class="docs-code">
<pre>
<span class="c-key">COMMAND_WHITELIST</span>=<span class="c-val">uptime,hostname,df -h,free -m,systemctl status *,ps aux</span></pre>
</div>
<h3>Execution Queue</h3>
<p>Executions are queued and limited to <code class="inline">CONCURRENT_EXECUTION_LIMIT</code> parallel operations to prevent resource exhaustion. Queue status is visible in the UI.</p>
</div>
<div class="docs-section" id="lifecycle">
<h2>VM Lifecycle Management</h2>
<p>For Proxmox and AWS nodes, the node detail page includes a <strong>Manage tab</strong> with lifecycle controls. Available actions depend on the current resource state:</p>
<table class="docs-table">
<thead><tr><th>State</th><th>Available Actions</th></tr></thead>
<tbody>
<tr><td>stopped</td><td>Start, Destroy</td></tr>
<tr><td>running</td><td>Stop, Shutdown, Reboot, Suspend (VMs), Destroy</td></tr>
<tr><td>suspended</td><td>Resume, Destroy</td></tr>
</tbody>
</table>
<ul>
<li>Destructive actions (Destroy, Terminate) require an explicit confirmation dialog</li>
<li>Each action requires the corresponding RBAC permission (e.g. <code class="inline">manage:vms</code>)</li>
<li>All lifecycle actions are recorded in the Node Journal and Execution History</li>
<li>Real-time status updates are shown during the operation</li>
</ul>
</div>
<div class="docs-section" id="history">
<h2>Execution History</h2>
<p>Every command, task, and package operation is stored in the SQLite database. The History page shows all runs with:</p>
<ul>
<li>Timestamp, duration, target nodes, integration used</li>
<li>Full command/task name and parameters</li>
<li>Per-node success/failure status with output</li>
<li>One-click <strong>re-run</strong> — replay any historical execution instantly</li>
</ul>
</div>
<div class="docs-section" id="node-journal">
<h2>Node Journal <span style="font-size:.72rem;background:rgba(52,211,153,.12);border:1px solid rgba(52,211,153,.3);color:var(--green);padding:.15rem .55rem;border-radius:100px;vertical-align:middle;font-weight:700;letter-spacing:.04em">v0.9+</span></h2>
<p>Every node has a personal journal — a chronological timeline of every action, lifecycle event, and operation that has touched it. Access it from the node detail page.</p>
<ul>
<li>Records command executions, task runs, package operations, and VM lifecycle actions</li>
<li>Timestamps, actor (which user), integration used, and outcome per entry</li>
<li>Persisted in SQLite — survives restarts, gives you long-term per-node history</li>
<li>Complements Execution History (which is fleet-wide) with a node-centric view</li>
</ul>
</div>
<div class="docs-section" id="rbac">
<h2>RBAC & Permissions</h2>
<p>Pabawi includes a full role-based access control system:</p>
<ul>
<li><strong>Users</strong> — individual accounts with JWT authentication</li>
<li><strong>Roles</strong> — named sets of permissions (e.g. <em>operator</em>, <em>viewer</em>, <em>admin</em>)</li>
<li><strong>Groups</strong> — users grouped for easier role assignment</li>
<li><strong>Permissions</strong> — granular: <code class="inline">execute:commands</code>, <code class="inline">read:inventory</code>, <code class="inline">manage:users</code>, etc.</li>
</ul>
<p>The default admin account is created on first start using <code class="inline">DEFAULT_ADMIN_USERNAME</code> and <code class="inline">DEFAULT_ADMIN_PASSWORD</code>. Change the password immediately after first login.</p>
</div>
<div class="docs-section" id="status-dashboard">
<h2>Integration Status Dashboard <span style="font-size:.72rem;background:rgba(99,102,241,.12);border:1px solid rgba(99,102,241,.3);color:var(--brand-light);padding:.15rem .55rem;border-radius:100px;vertical-align:middle;font-weight:700;letter-spacing:.04em">v1.0</span></h2>
<p>The Integration Status Dashboard gives you a real-time health view of every configured integration. Access it from the main navigation under <strong>Settings → Integrations</strong>.</p>
<ul>
<li>Shows enabled/disabled status and connection health for each integration</li>
<li><strong>Test Connection</strong> buttons for Proxmox and AWS — trigger an on-demand health check</li>
<li>Displays last-checked timestamp and error details if a connection fails</li>
<li>Read-only — configuration is managed via <code class="inline">.env</code>, not the UI</li>
<li>Append <code class="inline">?refresh=true</code> to the API health endpoint to force a fresh check bypassing cache</li>
</ul>
<div class="docs-callout tip">
<p><strong>Tip:</strong> Use the Status Dashboard after editing your <code class="inline">.env</code> and restarting to quickly confirm all integrations connected successfully before handing the system to the team.</p>
</div>
</div>
<div class="docs-section" id="setup-wizards">
<h2>Setup Wizards & .env Snippets <span style="font-size:.72rem;background:rgba(99,102,241,.12);border:1px solid rgba(99,102,241,.3);color:var(--brand-light);padding:.15rem .55rem;border-radius:100px;vertical-align:middle;font-weight:700;letter-spacing:.04em">v1.0</span></h2>
<p>Each integration has an in-browser setup wizard that guides you through configuration step by step and generates a ready-to-paste <code class="inline">.env</code> snippet.</p>
<h3>How it works</h3>
<ol style="padding-left:1.5rem;color:var(--text-muted);font-size:.95rem;line-height:2;margin-bottom:1rem">
<li>Navigate to <strong>Settings → Setup Guides</strong> and choose an integration</li>
<li>Fill in the form fields (host, credentials, paths, options)</li>
<li>The wizard generates the corresponding <code class="inline">.env</code> variable block</li>
<li>Click <strong>Copy to clipboard</strong> and paste into your <code class="inline">backend/.env</code></li>
<li>Restart Pabawi — the integration activates immediately</li>
</ol>
<div class="docs-callout">
<p><strong>Security note:</strong> Sensitive values (passwords, tokens, secret keys) are masked in the generated snippet preview. The snippet is never stored in the database — it exists only in your browser session until you copy it.</p>
</div>
<h3>Why .env only?</h3>
<p>From v1.0 onward, <code class="inline">backend/.env</code> is the single source of truth for all integration configuration. Previous versions allowed database-stored config overrides; this has been removed to simplify the mental model — what's in <code class="inline">.env</code> is exactly what runs.</p>
</div>
<div class="docs-section" id="expert-mode">
<h2>Expert Mode</h2>
<p>Toggle Expert Mode from the navigation bar to reveal:</p>
<ul>
<li>Full CLI command lines for every Bolt/Ansible/SSH operation</li>
<li>Raw API responses and timing information</li>
<li>Frontend debug logging in the browser console</li>
<li>Extended node detail data</li>
</ul>
<p>Expert Mode is per-session and does not require a page reload. Invaluable for debugging integration issues or verifying exactly what command is being run.</p>
</div>
<!-- ===== API ===== -->
<div class="docs-section" id="api">
<h2>API Reference</h2>
<p>Pabawi exposes a REST API at <code class="inline">/api/v1/</code>. All endpoints require a Bearer token when <code class="inline">AUTH_ENABLED=true</code>.</p>
<h3>Authentication</h3>
<div class="docs-code">
<pre>
<span class="c-cmt"># Login and get a token</span>
<span class="c-prompt">POST </span><span class="c-cmd">/api/v1/auth/login</span>
<span class="c-cmt">Body: { "username": "admin", "password": "..." }</span>
<span class="c-cmt"># Use the token in subsequent requests</span>
<span class="c-prompt">GET </span><span class="c-cmd">/api/v1/inventory</span>
<span class="c-cmt">Header: Authorization: Bearer <token></span></pre>
</div>
<h3>Key Endpoints</h3>
<table class="docs-table">
<thead><tr><th>Method + Path</th><th>Description</th></tr></thead>
<tbody>
<tr><td>GET /api/v1/inventory</td><td>List all nodes from all enabled integrations</td></tr>
<tr><td>GET /api/v1/inventory/:node</td><td>Get details and facts for a single node</td></tr>
<tr><td>POST /api/v1/execute/command</td><td>Run a command on target nodes</td></tr>
<tr><td>POST /api/v1/execute/task</td><td>Run a Bolt task on target nodes</td></tr>
<tr><td>GET /api/v1/executions</td><td>List execution history</td></tr>
<tr><td>GET /api/v1/executions/:id</td><td>Get a specific execution result</td></tr>
<tr><td>GET /api/v1/stream/:id</td><td>SSE stream for live execution output</td></tr>
<tr><td>GET /api/v1/reports</td><td>List Puppet run reports (PuppetDB required)</td></tr>
<tr><td>GET /api/v1/hiera/lookup</td><td>Resolve a Hiera key for a node</td></tr>
<tr><td>GET /api/v1/health</td><td>Integration health status</td></tr>
<tr><td>GET /api/v1/users</td><td>List users (admin only)</td></tr>
<tr><td>POST /api/v1/users</td><td>Create a user (admin only)</td></tr>
</tbody>
</table>
</div>
<!-- ===== ENV VARS ===== -->
<div class="docs-section" id="env-vars">
<h2>Environment Variables</h2>
<p>Complete reference for all configuration variables. See <code class="inline">backend/.env.example</code> in the repo for the annotated template.</p>
<div class="docs-code">
<pre>
<span class="c-cmt"># ── Core ──────────────────────────────────────────</span>
<span class="c-key">PORT</span>=<span class="c-val">3000</span>
<span class="c-key">HOST</span>=<span class="c-val">0.0.0.0</span>
<span class="c-key">LOG_LEVEL</span>=<span class="c-val">info</span>
<span class="c-key">DATABASE_PATH</span>=<span class="c-val">./data/pabawi.db</span>
<span class="c-cmt"># ── Authentication ────────────────────────────────</span>
<span class="c-key">AUTH_ENABLED</span>=<span class="c-val">true</span>
<span class="c-key">JWT_SECRET</span>=<span class="c-val">your-secret-here-min-32-chars</span>
<span class="c-key">DEFAULT_ADMIN_USERNAME</span>=<span class="c-val">admin</span>
<span class="c-key">DEFAULT_ADMIN_PASSWORD</span>=<span class="c-val">changeme</span>
<span class="c-cmt"># ── Execution ─────────────────────────────────────</span>
<span class="c-key">CONCURRENT_EXECUTION_LIMIT</span>=<span class="c-val">5</span>
<span class="c-key">COMMAND_WHITELIST_ENABLED</span>=<span class="c-val">true</span>
<span class="c-key">COMMAND_WHITELIST</span>=<span class="c-val">uptime,hostname,df -h,free -m</span>
<span class="c-cmt"># ── Bolt ──────────────────────────────────────────</span>
<span class="c-key">BOLT_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">BOLT_PROJECT_PATH</span>=<span class="c-val">/path/to/bolt-project</span>
<span class="c-cmt"># ── SSH ───────────────────────────────────────────</span>
<span class="c-key">SSH_ENABLED</span>=<span class="c-val">true</span>
<span class="c-key">SSH_DEFAULT_USER</span>=<span class="c-val">ubuntu</span>
<span class="c-key">SSH_DEFAULT_KEY_PATH</span>=<span class="c-val">~/.ssh/id_rsa</span>
<span class="c-key">SSH_INVENTORY</span>=<span class="c-val">[{"name":"web01","host":"192.168.1.10"}]</span>
<span class="c-cmt"># ── PuppetDB ──────────────────────────────────────</span>
<span class="c-key">PUPPETDB_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">PUPPETDB_URL</span>=<span class="c-val">https://puppet.example.com:8081</span>
<span class="c-key">PUPPETDB_SSL_CERT</span>=<span class="c-val">/etc/puppetlabs/puppet/ssl/certs/node.pem</span>
<span class="c-key">PUPPETDB_SSL_KEY</span>=<span class="c-val">/etc/puppetlabs/puppet/ssl/private_keys/node.pem</span>
<span class="c-key">PUPPETDB_SSL_CA</span>=<span class="c-val">/etc/puppetlabs/puppet/ssl/certs/ca.pem</span>
<span class="c-cmt"># ── Puppetserver ──────────────────────────────────</span>
<span class="c-key">PUPPETSERVER_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">PUPPETSERVER_URL</span>=<span class="c-val">https://puppet.example.com:8140</span>
<span class="c-cmt"># ── Hiera ─────────────────────────────────────────</span>
<span class="c-key">HIERA_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">HIERA_CONTROL_REPO_PATH</span>=<span class="c-val">/path/to/control-repo</span>
<span class="c-cmt"># ── Ansible ───────────────────────────────────────</span>
<span class="c-key">ANSIBLE_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">ANSIBLE_PROJECT_PATH</span>=<span class="c-val">/path/to/ansible-project</span>
<span class="c-key">ANSIBLE_INVENTORY_PATH</span>=<span class="c-val">/path/to/inventory</span>
<span class="c-cmt"># ── Proxmox ───────────────────────────────────────</span>
<span class="c-key">PROXMOX_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">PROXMOX_HOST</span>=<span class="c-val">proxmox.example.com</span>
<span class="c-key">PROXMOX_PORT</span>=<span class="c-val">8006</span>
<span class="c-key">PROXMOX_TOKEN</span>=<span class="c-val">user@realm!tokenid=uuid</span>
<span class="c-key">PROXMOX_SSL_VERIFY</span>=<span class="c-val">true</span>
<span class="c-key">PROXMOX_TIMEOUT</span>=<span class="c-val">30000</span>
<span class="c-cmt"># ── AWS ───────────────────────────────────────────</span>
<span class="c-key">AWS_ENABLED</span>=<span class="c-val">false</span>
<span class="c-key">AWS_ACCESS_KEY_ID</span>=<span class="c-val">AKIAIOSFODNN7EXAMPLE</span>
<span class="c-key">AWS_SECRET_ACCESS_KEY</span>=<span class="c-val">wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</span>
<span class="c-key">AWS_DEFAULT_REGION</span>=<span class="c-val">us-east-1</span>
<span class="c-key">AWS_REGIONS</span>=<span class="c-val">["us-east-1","eu-west-1"]</span></pre>
</div>
</div>
<!-- ===== TROUBLESHOOTING ===== -->
<div class="docs-section" id="troubleshooting">
<h2>Troubleshooting</h2>
<h3>Inventory is empty</h3>
<p>Check that at least one integration is enabled (<code class="inline">*_ENABLED=true</code>) and that the CLI tools are on the PATH. Enable <code class="inline">LOG_LEVEL=debug</code> and check server logs. In the UI, toggle Expert Mode to see integration health status.</p>
<h3>Command execution fails</h3>
<ul>
<li>Verify the command is in the whitelist (or disable whitelist temporarily for testing)</li>
<li>Check that the Pabawi server has network access to target nodes</li>
<li>For SSH: verify key-based auth works from the Pabawi server manually</li>
<li>For Bolt: run the command manually with <code class="inline">bolt command run "..." --targets ...</code></li>
</ul>
<h3>PuppetDB SSL errors</h3>
<p>Ensure the certificate paths are correct and that the Pabawi server's certificate is signed by the same CA as PuppetDB. The pabawi process user must have read access to the key files.</p>
<h3>Docker permission errors</h3>
<p>Use <code class="inline">--user "$(id -u):1001"</code> to run the container as your user (group 1001 is the node user inside the container). Ensure mounted volumes are owned by that UID.</p>
<div class="docs-callout">
<p><strong>Still stuck?</strong> Open a <a href="https://github.com/example42/pabawi/issues" target="_blank" rel="noopener">GitHub issue</a> with your Pabawi version, sanitized config, reproduction steps, and the error from <code class="inline">LOG_LEVEL=debug</code> output.</p>
</div>
</div>
</main>
</div>
<!-- FOOTER -->
<footer class="footer">
<div class="container">
<div class="footer-bottom" style="border-top:none;padding-top:0">
<p>© 2025–2026 <a href="https://example42.com" target="_blank" rel="noopener">example42</a> · Apache 2.0 License</p>
<div class="footer-links">
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="https://github.com/example42/pabawi" target="_blank" rel="noopener">GitHub</a>
</div>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>