InfraDocs Documentation

Self-hosted infrastructure documentation platform. Encrypted credential vault, full audit trail, role-based access — everything you need to document and secure your IT environment.

🏢

Multi-Site

Organize all assets by site — datacenters, offices, colo, cloud regions.

🔐

Encrypted Vault

AES-256-GCM encryption for credentials. Rate-limited reveals with full audit trail.

🛡️

Audit Everything

Every create, update, delete, and credential reveal is logged with user, IP, and timestamp.

🔍

Instant Search

Full-text search across all entities with Ctrl+K. Find anything in milliseconds.

🔥

Firewall Docs

Document firewall clusters with rules, interfaces, NAT policies, and VPN tunnels.

📊

Expiration Dashboard

Track certificate, contract, and credential expiration dates at a glance. Never miss a renewal.

📥

Import / Export

Bulk import from CSV. Export any table to XLSX or CSV for reporting and migration.

🔗

Asset Linking

Create relationships between any assets. See what connects to what, what backs up what.


Quick Start (Try It Locally)

Want to kick the tires on your laptop before setting up a server? This gets InfraDocs running on your machine in about 2 minutes. You need Node.js 20 or higher installed — that's it.

💻
Don't have Node.js? Download it from nodejs.org — grab the LTS version, run the installer, and you're good.
1

Download InfraDocs

Open a terminal (Command Prompt, PowerShell, or Terminal on Mac) and run:

git clone https://github.com/thekennyriley/infra-docs.git
cd infra-docs

This downloads the entire project into a folder called infra-docs.

2

Install dependencies

Still in your terminal, run these two commands. They download the libraries InfraDocs needs:

cd backend && npm install && cd ..
cd frontend && npm install && cd ..

This takes about 30 seconds. You'll see a bunch of text scroll by — that's normal.

3

Start InfraDocs

You need two terminal windows — one for the backend (the brains) and one for the frontend (the UI).

Terminal 1 — start the backend:

cd backend
node server.js

You should see: InfraDocs API running on port 3001 — leave this running.

Terminal 2 — start the frontend:

cd frontend
npx vite

You should see a URL like http://localhost:3000 — leave this running too.

4

Open it in your browser

Go to http://localhost:3000

Log in with:

UsernamePassword
adminadmin

It will ask you to set a new password — pick something you'll remember. After that, you're in! Start adding your infrastructure.

That's it. Your data is saved in a file called backend/data/infra.db. Encryption keys are auto-generated for dev mode. When you're ready to put this on a real server, follow the Production guide below.

Production Installation

This sets up InfraDocs on a real Linux server so your team can access it 24/7. We'll walk through every single step. Total time: about 10 minutes.

What you need

💡
Never done this before? That's fine. SSH into your server and follow along — every command is copy-paste ready. Lines starting with # are explanatory comments, not commands.

Step 1: Install the required software

SSH into your server and run this block. It installs Node.js (runs the app), nginx (serves it to the web), and git (downloads the code):

# Add the Node.js 22 package source
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -

# Install everything
sudo apt-get install -y nodejs nginx git

# Verify it worked (you should see version numbers)
node --version
nginx -v
git --version

Step 2: Download and build InfraDocs

# Go to your home directory
cd ~

# Download InfraDocs
git clone https://github.com/thekennyriley/infra-docs.git
cd infra-docs

# Install backend libraries
cd backend && npm install --production && cd ..

# Install frontend libraries and build the UI
cd frontend && npm install && npm run build && cd ..

The npm run build step compiles the frontend into static files. You should see ✓ built in X seconds at the end.

Step 3: Generate your secret keys

InfraDocs needs two secret keys: one for login sessions, one for encrypting stored passwords. Run these two commands and save the output — you'll need it in the next step:

# Generate your JWT key (for login sessions)
echo "JWT_SECRET: $(openssl rand -hex 32)"

# Generate your encryption key (for the credential vault)
echo "ENCRYPTION_KEY: $(openssl rand -hex 32)"

Each command prints a long random string like a1b2c3d4e5f6.... Copy both.

Step 4: Create the configuration file

This tells InfraDocs where to find its database and what keys to use. Replace the placeholder values with your actual keys from Step 3:

# Create the config file
nano ~/infra-docs/.env

Paste this into the editor. Replace the two placeholder lines with your actual keys:

NODE_ENV=production
PORT=3001
DB_PATH=/home/YOUR_USERNAME/infra-docs/backend/data/infra.db

# Paste your keys from Step 3 here:
JWT_SECRET=paste-your-jwt-key-here
ENCRYPTION_KEY=paste-your-encryption-key-here

# Your server's address (change this to your domain or IP):
CORS_ORIGINS=http://your-server-ip

Save and exit nano: press Ctrl+X, then Y, then Enter.

Now lock down the file so only you can read it:

chmod 600 ~/infra-docs/.env
🚨
Don't skip the keys. In production mode, InfraDocs refuses to start without real keys. This protects you from accidentally running with insecure defaults.
⚠️
Back up your ENCRYPTION_KEY somewhere safe (password manager, printed in a safe, etc). If you lose it, every password stored in InfraDocs becomes permanently unrecoverable. The key is not stored in the database — it only exists in your .env file.

Step 5: Set up the auto-start service

This makes InfraDocs start automatically when your server boots and restart if it ever crashes:

sudo tee /etc/systemd/system/infra-docs.service > /dev/null << EOF
[Unit]
Description=InfraDocs Backend API
After=network.target

[Service]
Type=simple
User=$USER
WorkingDirectory=$HOME/infra-docs/backend
EnvironmentFile=$HOME/infra-docs/.env
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

# Tell the system about the new service
sudo systemctl daemon-reload

# Enable it (start on boot)
sudo systemctl enable infra-docs

# Start it right now
sudo systemctl start infra-docs

# Check that it's running
sudo systemctl status infra-docs

You should see active (running) in green. If you see an error, check the Troubleshooting section.

Step 6: Set up the web server

nginx sits in front of InfraDocs and serves it to your browser. This config handles both the UI and the API:

# Create the nginx config
sudo tee /etc/nginx/sites-available/infra-docs > /dev/null << EOF
server {
    listen 80;
    server_name _;

    # This serves the frontend UI
    root $HOME/infra-docs/frontend/dist;
    index index.html;

    location / {
        try_files \$uri \$uri/ /index.html;
    }

    # This forwards API requests to the backend
    location /api/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }

    # Security headers
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options DENY always;

    # Compression (makes pages load faster)
    gzip on;
    gzip_types text/plain text/css application/javascript application/json;
}
EOF

# Activate the config
sudo ln -sf /etc/nginx/sites-available/infra-docs /etc/nginx/sites-enabled/

# Remove the default "Welcome to nginx" page
sudo rm -f /etc/nginx/sites-enabled/default

# Test the config (should say "syntax is ok")
sudo nginx -t

# Apply it
sudo systemctl reload nginx

Step 7: Open it up!

Open your browser and go to:

http://your-server-ip

You should see the InfraDocs login page. Log in with:

UsernamePassword
adminadmin

It will immediately ask you to set a new password. Pick something strong — this is your admin account.

🎉
You're done! InfraDocs is running, auto-starts on boot, and is ready for your team. Keep reading for optional HTTPS setup and team onboarding.

Optional: Add HTTPS (recommended)

If you have a domain name pointed at your server, you can add free HTTPS encryption in one command:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com

Certbot will ask for your email, agree to terms, and automatically configure nginx for HTTPS. It also sets up auto-renewal so your certificate never expires.

Optional: Lock down with a firewall

Only allow SSH and web traffic — block everything else:

sudo ufw allow 22    # SSH (so you can still log in)
sudo ufw allow 80    # HTTP
sudo ufw allow 443   # HTTPS
sudo ufw enable      # Turn on the firewall

Dashboard

The dashboard is your at-a-glance overview of everything documented in InfraDocs. It shows:

Use the dashboard as your daily check-in. If something's expired or about to expire, you'll see it here first.


Sites

Sites are the top-level organizational unit. Every asset in InfraDocs can be linked to a site.

A site represents a physical or logical location: a datacenter, office, colo facility, cloud region, or branch office.

Fields

FieldDescription
nameDisplay name (e.g., "PHX-DC1", "AWS us-west-2")
codeShort unique code used in naming conventions (e.g., "PHX1")
site_typedatacenter, office, colocation, cloud, remote, branch
address / city / state / countryPhysical location details
statusactive, inactive, decommissioned
notesFree-form notes
💡
When you create other assets (servers, network devices, etc.), you'll select which site they belong to from a dropdown. This keeps everything organized and lets you filter views by site.

Network Assets

Document switches, routers, firewalls, access points, load balancers, VPN gateways — any network device.

Fields

FieldDescription
nameDevice name / hostname
asset_typeswitch, router, firewall, access_point, load_balancer, vpn_gateway, etc.
manufacturer / modelHardware details (e.g., Cisco, Arista, Palo Alto)
serial_numberFor warranty and support lookups
ip_address / mgmt_ipProduction and management IPs
firmware_versionCurrent firmware/OS version
location / rack_unitPhysical location (e.g., "Row 3, Rack 12, U24-26")
ha_roleHA role: primary, secondary, standalone
site_idWhich site this device belongs to

Network assets can be linked to a firewall cluster for grouped firewall documentation, and can be linked to credentials for device login details.


Firewall Clusters

Firewall clusters let you group firewall devices together and document their complete configuration: rules, interfaces, NAT policies, and VPN tunnels.

Cluster detail view

Click into any firewall cluster to see four tabs:

Rules

Document your firewall policy in order. Each rule includes:

Interfaces

Document each interface with zone assignment, IP address, subnet, VLAN ID, and status.

NAT Policies

Source NAT, destination NAT, and static NAT — document the original and translated addresses/ports.

VPN Tunnels

Site-to-site and remote access VPN documentation: peer IPs, local/remote networks, IKE version, encryption settings, and tunnel status.


Networks & IPAM

The Networks page provides IP Address Management (IPAM) — document your subnets, VLANs, and individual IP assignments.

Subnets

FieldDescription
nameSubnet name (e.g., "Server VLAN", "Guest WiFi")
subnetNetwork address (e.g., 10.0.1.0)
cidrPrefix length (e.g., 24)
vlan_id / vlan_nameVLAN number and name
gatewayDefault gateway IP
dns_primary / dns_secondaryDNS server addresses
dhcp_enabled / dhcp_start / dhcp_endDHCP scope details

IP Addresses

Click into any subnet to manage individual IP assignments:


Servers & VMs

Document physical hosts, virtual machines, and containers. The Servers page includes a hierarchical view: management platforms → clusters → hosts → VMs.

Management Platforms

Document vCenter, Proxmox, Hyper-V Manager, or any hypervisor management platform with its URL, version, and license details.

Clusters

Compute clusters with HA/DRS settings, total CPU/RAM capacity, and linked management platform.

Servers

FieldDescription
nameServer hostname
server_typephysical, vm, container, hypervisor
os / os_versionOperating system details
ip_addressPrimary IP
cpu_cores / ram_gbCompute specs
cluster_idWhich cluster this server belongs to
host_idFor VMs: which physical host they run on
purpose / ownerWhat it does and who owns it

Storage

Document SANs, NAS arrays, DAS, object storage, and hyperconverged storage.

FieldDescription
nameArray/system name
storage_typeSAN, NAS, DAS, object, cloud
manufacturer / modelHardware details
capacity_tb / used_tbTotal and used capacity
protocoliSCSI, NFS, FC, SMB, S3
ip_addressManagement IP

Backup Jobs

Track all backup jobs: what's being backed up, where, how often, and whether it's working.

FieldDescription
nameJob name
backup_typefull, incremental, differential, snapshot
softwareVeeam, Commvault, rsync, AWS Backup, etc.
source / targetWhat's being backed up and where to
scheduleCron expression or description
retentionHow long backups are kept
rpo_hoursRecovery Point Objective
last_run / last_statusWhen it last ran and whether it succeeded
💡
Failed backups appear on the dashboard. Update last_run and last_status regularly (or automate it via the API) to keep your dashboard accurate.

Cloud & SaaS

Document cloud tenants and SaaS subscriptions: Azure, AWS, GCP, M365, Google Workspace, and any other cloud service.

FieldDescription
nameTenant/account name
providerAzure, AWS, GCP, M365, Google Workspace, etc.
tenant_idAzure tenant GUID, AWS account ID, etc.
primary_domainPrimary domain for the tenant
subscription_type / license_countSubscription tier and seat count
admin_urlDirect link to the admin portal

Credentials

The encrypted credential vault. Store passwords, API keys, service account credentials, and SSH keys with AES-256-GCM encryption at rest.

How it works

Fields

FieldDescription
nameCredential name (e.g., "vCenter Admin", "AWS Root")
credential_typelocal, domain, service_account, api_key, certificate, ssh_key
usernameLogin username
passwordSecret (encrypted at rest)
urlLogin URL or portal
rotation_daysHow often this credential should be rotated (default: 90)
expires_atAuto-calculated expiration date based on rotation schedule
linked_asset_type / linked_asset_idLink to a server, network device, or cloud tenant
vault_path / vault_engineFor HashiCorp Vault references (pointer, not stored locally)
⚠️
Credential reveals are rate-limited to 10 per minute per user. Only admin and engineer roles can reveal passwords.

Vendor Contracts

Track support contracts, software licenses, maintenance agreements, and SaaS subscriptions with their renewal dates and costs.

FieldDescription
vendor_nameVendor/company name
contract_typesupport, license, maintenance, SaaS, consulting
contract_numberContract/PO number
start_date / end_dateContract period
annual_costAnnual cost ($)
renewal_typeauto, manual, multi-year
support_levele.g., 24x7, 8x5 NBD, Premium
support_phone / support_email / support_portalHow to get help
account_repYour account rep's name

Contracts expiring within 90 days appear on the dashboard with urgency color-coding.


DR & Compliance

Document disaster recovery plans with RTO/RPO objectives, recovery steps, dependencies, and test schedules.

FieldDescription
namePlan name (e.g., "PHX-DC1 Full Site Failover")
site_idWhich site this plan covers
rto_hours / rpo_hoursRecovery Time/Point Objectives
prioritycritical, high, medium, low
recovery_stepsStep-by-step recovery procedure
dependenciesWhat must be restored first
last_tested / next_testDR test schedule

Press Ctrl+K (or +K on Mac) anywhere in the app to open the search palette.

Search is powered by SQLite FTS5 (full-text search). It indexes names, IPs, hostnames, serial numbers, descriptions, notes, and other key fields across all entity types.

Results show the entity type, name, and a highlighted snippet of the matching text. Click any result to jump directly to it.


Import / Export

Import (CSV)

  1. Navigate to Import in the sidebar
  2. Select the entity type (sites, servers, credentials, etc.)
  3. Upload a CSV file
  4. Preview the first 5 rows and map CSV columns to database fields
  5. Click Import — rows are inserted in a transaction (all or nothing on errors)

Maximum file size: 50MB. UTF-8 and Latin-1 encodings are supported.

Export (XLSX / CSV)

From any entity list page, you can export the data. Supported formats:

Credential passwords are automatically masked as [ENCRYPTED] in exports — they are never included in plaintext.


Audit Log

The audit log records every action in InfraDocs:

Each entry includes: timestamp, username, user ID, action, resource type, resource ID, details (JSON), and IP address.

👁️
Only admin and auditor roles can view the full audit log. Other users see nothing.

User Management

Admins can create, edit, and delete users from Settings.

Creating users

Set the username, display name, password, and role. New users can optionally be flagged to change their password on first login.

Changing passwords

Users can change their own password. Admins can reset any user's password. Minimum length: 8 characters.

Deactivating users

Set a user's account to inactive to revoke access without deleting them. Their audit history is preserved.


Environment Variables

VariableRequiredDefaultDescription
JWT_SECRETRequiredDev fallbackSigns auth tokens. openssl rand -hex 32
ENCRYPTION_KEYRequiredDev fallbackAES-256-GCM key for credentials. openssl rand -hex 32
CORS_ORIGINSNohttp://localhost:3000Comma-separated allowed origins
PORTNo3001Backend API port
DB_PATHNo./data/infra.dbSQLite database file path
NODE_ENVNoSet to production to enforce required secrets

Roles & Permissions

Permission admin engineer auditor readonly
View all assets
Create / edit assets
Delete assets
Reveal credentials
Import data
Export data
View audit log
Manage users

Security

Encryption

Access control

Audit trail

Recommendations


Backup & Restore

What to back up

Two files contain your entire InfraDocs state:

  1. backend/data/infra.db — the SQLite database (all data)
  2. .env — your encryption key and JWT secret
🚨
If you lose your ENCRYPTION_KEY, all stored credential secrets are permanently unrecoverable. Store it in a password manager, offline USB, or printed in a safe.

Backup command

# Copy database
cp backend/data/infra.db ~/backups/infra-docs-$(date +%F).db

# Copy env (contains your encryption key!)
cp .env ~/backups/infra-docs-env-$(date +%F)

Automated daily backup

# Add to crontab (crontab -e)
0 2 * * * cp /home/$USER/infra-docs/backend/data/infra.db \
  /home/$USER/backups/infra-docs-$(date +\%F).db && \
  find /home/$USER/backups -name "infra-docs-*.db" -mtime +30 -delete

Restore

sudo systemctl stop infra-docs
cp ~/backups/infra-docs-2026-03-01.db backend/data/infra.db
sudo systemctl start infra-docs

Updating

cd /home/$USER/infra-docs

# Pull latest
git pull

# Rebuild
cd backend && npm install --production && cd ..
cd frontend && npm install && npm run build && cd ..

# Restart
sudo systemctl restart infra-docs

The database schema auto-migrates on startup via safeAddColumn() — no manual migration steps needed. New tables are created with CREATE TABLE IF NOT EXISTS, and new columns are added non-destructively.


API Reference

InfraDocs exposes a RESTful JSON API. All endpoints require a Bearer JWT token (obtained from /api/auth/login).

Authentication

# Login — returns JWT token
POST /api/auth/login
{ "username": "admin", "password": "your-password" }

# Response
{ "token": "eyJ...", "user": { "id": 1, "username": "admin", "role": "admin" } }

# Use the token in subsequent requests
Authorization: Bearer eyJ...

CRUD endpoints

Every entity type follows the same pattern:

MethodPathDescriptionRole
GET/api/{entity}List (paginated, filterable)Any authenticated
GET/api/{entity}/:idGet one (with linked assets)Any authenticated
POST/api/{entity}Createadmin, engineer
PUT/api/{entity}/:idUpdateadmin, engineer
DELETE/api/{entity}/:idDeleteadmin only

Entity paths

/api/sites · /api/network-assets · /api/servers · /api/clusters · /api/management-platforms · /api/storage · /api/backup-jobs · /api/cloud-tenants · /api/credentials · /api/vendor-contracts · /api/dr-plans · /api/ipam · /api/ip-addresses · /api/certificates · /api/firewall-clusters

List query parameters

ParamDefaultDescription
page1Page number
limit50Items per page (max 200)
sortidSort column
orderdescasc or desc
site_idFilter by site
statusFilter by status
searchFull-text search

Credential-specific endpoints

# Reveal a credential (rate-limited, audited)
GET /api/credentials/:id/reveal
# → { "password": "the-secret", "source": "local" }

# List credentials linked to an asset
GET /api/credentials?linked_asset_type=servers&linked_asset_id=5

Firewall cluster sub-resources

GET/POST/PUT/DELETE /api/firewall-clusters/:id/rules
GET/POST/PUT/DELETE /api/firewall-clusters/:id/interfaces
GET/POST/PUT/DELETE /api/firewall-clusters/:id/nat
GET/POST/PUT/DELETE /api/firewall-clusters/:id/vpn

Search

GET /api/search?q=web-server&type=servers&limit=20

Import / Export

# Export to XLSX or CSV
GET /api/export/servers?format=xlsx
GET /api/export/credentials?format=csv

# Import preview (returns column headers + first 5 rows)
POST /api/import/preview  # multipart: file

# Import with column mapping
POST /api/import/servers  # multipart: file + mapping JSON

Asset links

# Create a link between two assets
POST /api/links
{ "source_type": "servers", "source_id": 1, "target_type": "storage", "target_id": 3, "link_type": "uses" }

# Get all links for an asset
GET /api/links/servers/1

Troubleshooting

App won't start in production

FATAL: JWT_SECRET environment variable is required in production.

Set JWT_SECRET and ENCRYPTION_KEY in your .env file and make sure the systemd service uses EnvironmentFile=.

Frontend shows blank page

# Verify the frontend is built
ls frontend/dist/index.html

# Verify nginx config
sudo nginx -t

# Check nginx is pointing to the right directory
grep root /etc/nginx/sites-enabled/infra-docs

Credential decryption fails

If you changed your ENCRYPTION_KEY after storing credentials, the old ones can't be decrypted. Always use the same key, or re-enter credentials after a key change.

Search returns no results

The FTS5 index is rebuilt automatically on create/update/delete. If it gets out of sync, restart the backend — the search index is rebuilt on boot if needed.

Check logs

# Backend logs
sudo journalctl -u infra-docs -n 50 --no-pager

# nginx logs
sudo tail -f /var/log/nginx/error.log

# What's on port 3001?
ss -tlnp | grep 3001

InfraDocs — Built by ATADAY