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)
Mode 2: Web + Hosted Vault (Recommended)
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):
- ✅ Deploy HTML Builder as static files
- ✅ Deploy Vault to Heroku/Railway/Fly.io
- ✅ Point HTML Builder to hosted vault
- ✅ 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):
- ✅ Bundle HTML Builder into app
- ✅ Bundle Vault into app (runs on startup)
- ✅ PyInstaller creates .app/.exe
- ✅ 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!
