Vault Deployment | 🎯 Clear Architecture: Web vs Desktop

Three Deployment Modes:

Mode 1: Pure Web (No Vault Needed)

User Browser
    ↓
https://yoursite.com/html-builder
    ↓
localStorage (editing)
    ↓
Browser FTP library (publishing)
    ↓
User's FTP server

Who: Public users on your site
Install: Nothing
Vault: Not needed (browser handles FTP)


User Browser
    ↓
https://yoursite.com/html-builder
    ↓
localStorage (editing)
    ↓
POST to yoursite.com/vault/api/ftp-save
    ↓
Your hosted Vault (FastAPI on Heroku/Railway)
    ↓
User's FTP server

Who: Public users on your site
Install: Nothing
Vault: You host it as web service
Why: More secure (FTP credentials go through your server, not browser)


Mode 3: Desktop App (ACCID)

User's Computer
    ↓
ACCID.app (double-click)
    ↓
Embedded HTML Builder + Waypoint
    ↓
Embedded Vault (localhost:9848)
    ↓
User's FTP server

Who: Desktop ACCID users
Install: Download .app/.exe, run it
Vault: Bundled inside desktop app (invisible to user)
Why: Full offline capability


FTP Publishing – Three Approaches:

Approach 1: Browser-Only (No Vault)

// In html-builder when deployed to web
// Use a browser FTP library

import FTPClient from 'basic-ftp';

async function publishSite(credentials, files) {
    const client = new FTPClient();
    await client.access({
        host: credentials.host,
        user: credentials.user,
        password: credentials.pass
    });
    
    for (const file of files) {
        await client.uploadFrom(file.content, file.path);
    }
}

Pros: No server needed
Cons: FTP credentials in browser (less secure), browser CORS issues


Approach 2: Vault as Web Service

// In html-builder when deployed to web
// Call your hosted vault

async function publishSite(credentials, files) {
    const response = await fetch('https://yoursite.com/vault/api/ftp-save', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            host: credentials.host,
            user: credentials.user,
            pass: credentials.pass,
            files: files
        })
    });
    
    return await response.json();
}

Pros: More secure, handles CORS, easier to debug
Cons: You need to host the vault


Approach 3: Desktop Bundle

// In ACCID desktop app
// Call local vault

async function publishSite(credentials, files) {
    const response = await fetch('http://localhost:9848/api/ftp-save', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            host: credentials.host,
            user: credentials.user,
            pass: credentials.pass,
            files: files
        })
    });
    
    return await response.json();
}

Pros: Works offline, no CORS issues, secure
Cons: Desktop-only


Unified Code – Works in All Modes:

// universal-vault-client.js

class VaultClient {
    constructor() {
        this.detectMode();
    }
    
    detectMode() {
        // Desktop app (vault on localhost)
        if (window.location.protocol === 'file:' || 
            window.location.hostname === 'localhost') {
            this.mode = 'desktop';
            this.vaultUrl = 'http://localhost:9848';
        }
        // Web deploy (use hosted vault)
        else {
            this.mode = 'web';
            this.vaultUrl = window.location.origin + '/vault';
        }
    }
    
    async publishToFTP(credentials, files) {
        const response = await fetch(`${this.vaultUrl}/api/ftp-save`, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({...credentials, files})
        });
        
        return await response.json();
    }
    
    async testConnection(credentials) {
        const response = await fetch(`${this.vaultUrl}/api/ftp-test`, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(credentials)
        });
        
        return await response.json();
    }
}

// Use it everywhere
const vault = new VaultClient();
await vault.publishToFTP(credentials, files);

Deployment Checklist:

For Web Users (yoursite.com):

  1. ✅ Deploy HTML Builder as static files
  2. ✅ Deploy Vault to Heroku/Railway/Fly.io
  3. ✅ Point HTML Builder to hosted vault
  4. ✅ Users visit site, edit, publish (no install)

File structure on web:

yoursite.com/
├── html-builder/           # Static HTML/JS
│   ├── index.html
│   ├── creator.js
│   └── universal-vault-client.js
└── vault/                  # FastAPI service
    └── (deployed separately to Heroku)

For Desktop Users (ACCID):

  1. ✅ Bundle HTML Builder into app
  2. ✅ Bundle Vault into app (runs on startup)
  3. ✅ PyInstaller creates .app/.exe
  4. ✅ Users download, run, everything works offline

File structure in .app:

ACCID.app/
├── html-builder/
├── waypoint/
└── vault.py (auto-starts on app launch)

Your Question Answered:

“How do I connect the two for users who cannot install the desktop app?”

Answer: You host the Vault as a web service. They don’t install anything.

Public Web Users:
    Browser → yoursite.com/html-builder → yoursite.com/vault → FTP
    (No installation, all in browser)

Desktop Users:
    ACCID.app → Embedded vault → FTP
    (Download app, no setup)

Same HTML Builder code works for both! The universal-vault-client.js detects the environment and uses the right vault URL.


Example: Hosting the Vault on Railway:

# 1. Create Procfile
echo "web: uvicorn vault:app --host 0.0.0.0 --port $PORT" > Procfile

# 2. Create requirements.txt
cat > requirements.txt << EOF
fastapi
uvicorn
python-multipart
EOF

# 3. Deploy to Railway
railway init
railway up

# 4. Get URL: https://your-vault.railway.app

Then in your HTML Builder:

const vault = new VaultClient();
// Auto-detects:
//   Desktop: http://localhost:9848
//   Web: https://your-vault.railway.app

Summary:

User Type What They Use Vault Location Installation
Web users Browser Your web server None
Desktop users ACCID app Bundled inside app Download app

No confusion! The vault can be:

  • A web service (for web users)
  • A localhost service (for desktop users)
  • The same Python code works for both!