UGA HOST Developer Docs
QSSN PaaS is a secure cloud network that hosts and manages UGA HOST, all developed by Gaston Software Solutions LLP. It provides developers with infrastructure to deploy, manage, and scale web applications with enterprise-grade security — handling both frontend sites and backend APIs from a single platform.
Overview
UGA HOST is the deployment engine inside QSSN PaaS. It covers two distinct hosting surfaces:
| Surface | What you deploy | How |
|---|---|---|
| Backend | Node.js Workers, Python containers | ugahost CLI — init + deploy |
| Frontend | Static sites, React/Vue/Angular apps | QSSN PaaS dashboard — connect GitHub repo & click Deploy |
Every project — backend or frontend — gets a live URL under gss-tec.com, automatic HTTPS, and access to the full management dashboard at qssn-cloud-manager.pages.dev.
Installation
Install the UGA HOST CLI globally via npm:
npm install -g ugahost@latest
Verify the installation:
ugahost --version
The CLI is open source. View the source code, report issues, or contribute on GitHub:
github.com/GSS-creator/UGA-HOST
Authentication
UGA HOST uses a two-layer authentication model. Your account is tied to GitHub OAuth — that is how you sign in to the dashboard. Your CLI and API access is controlled by a personal API key that you generate from the dashboard and paste into ugahost login.
| Layer | Method | Used for |
|---|---|---|
| Account login | GitHub OAuth | Dashboard at qssn-cloud-manager.pages.dev |
| CLI / API access | ugahost_ API key | ugahost login, all CLI commands, direct API calls |
GitHub OAuth Flow
Every UGA HOST account is backed by a GitHub identity. There is no username/password registration — GitHub is the only sign-in method.
Full flow
github.com/login/oauth/authorize with scopes user:email and repo. A one-time CSRF state token is stored in the database before the redirect.
/api/auth/github/callback. The platform verifies the state, exchanges the code for a GitHub access token, and fetches your GitHub profile and primary verified email.
developers table. If no account exists it is created automatically with a starter subscription, and a Welcome email is sent to your address.
id, email, tier) and appended to the redirect URL back to the dashboard. A Login notification email is sent with your IP and timestamp.
Authorization: Bearer <jwt>.
Emails sent during account lifecycle
| Event | Contents | |
|---|---|---|
| First sign-in (account created) | Welcome email | Account confirmation, link to dashboard, getting-started guide |
| Every subsequent sign-in | Login notification | Time of login, IP address / auth method (GitHub OAuth) |
| API key created | API key created email | Key name, preview of first characters, expiry date, permissions |
| API key revoked | API key revoked email | Key name, revocation timestamp |
| Deploy succeeds | Deploy success email | Project name, subdomain URL, version ID, deploy source (CLI / Editor) |
| Deploy fails | Deploy failed email | Project name, error message |
API Keys
API keys are what authenticate the CLI and any direct HTTP calls you make to the platform API. They are separate from your JWT session token.
Format
Every API key is prefixed with ugahost_ followed by 64 hex characters (32 random bytes):
ugahost_a3f8c2e1d4b7a9f0e2c4d6b8a1f3e5d7c9b2a4e6f8d0b2c4a6e8f0d2b4c6a8
How to create an API key
| Field | Description |
|---|---|
| Key name | A label for this key, e.g. laptop-dev or CI/CD pipeline |
| Expiry | Number of days until the key expires — or leave blank for no expiry |
| Permissions | read_write (default) — full access to deploy, manage env vars, query DB |
Key validation on every request
Every CLI command sends Authorization: Bearer ugahost_…. The platform middleware:
- Detects the
ugahost_prefix and routes toAPIKeyManager.validateAPIKey() - Queries
api_keysWHEREapi_key = ?ANDis_active = 1 - Checks
expires_at— rejects if the current time is past expiry - Updates
last_used_at = datetime('now')on the matched row - Returns the
developer_idandemailso the rest of the request proceeds as that developer
Revoking a key
In the dashboard go to Settings → API Keys, find the key row, and click Revoke. This sets is_active = 0 — the key stops working immediately on the next request. A revocation email is sent to your account. The row is kept in the database (soft-delete) so audit history is preserved. Use Delete to remove it permanently.
CLI Login — ugahost login
Run ugahost login to authenticate the CLI. It prompts for your email and API key, validates them against the platform, and saves the credentials locally:
ugahost login
# Prompts:
# Email: you@example.com
# API Key: **************** (input masked)
What happens under the hood
ugahost_.
POST request is made to /api/auth/validate-api-key with { email, api_key }. The platform checks the key exists, is active, is not expired, and that the email matches the developer record.
~/.ugahost/config.json:
{
"email": "you@example.com",
"apiKey": "ugahost_a3f8c2…",
"apiUrl": "https://qssn-paas-management.gastonsoftwaresolutions234.workers.dev"
}
Authorization: Bearer <apiKey> on every HTTP request to the platform.
~/.ugahost/config.json out of source control. It contains your full API key in plain text. Add it to your global .gitignore: echo ~/.ugahost/config.json >> ~/.gitignore_globalugahost init
Run ugahost init inside your project directory. It will interactively ask:
| Prompt | Description | Example |
|---|---|---|
| Project name | Display name for your project | my-api |
| Subdomain | Lowercase letters, numbers, hyphens only. Becomes subdomain.gss-tec.com | my-api |
| Language | nodejs or python | nodejs |
| Port | The port your server listens on (Python only — used for container routing) | 3000 |
This writes a ugahost.json file to your project directory:
{
"name": "my-api",
"subdomain": "my-api",
"language": "nodejs",
"port": 3000,
"pythonMode": "standard"
}
After the first deploy, a projectId field is added automatically. This ID is used for all subsequent redeploys and API operations.
Deploy Flow
Running ugahost deploy executes this pipeline:
The CLI distinguishes between a first deploy and a redeploy:
| Condition | Endpoint called | Effect |
|---|---|---|
No projectId in ugahost.json | POST /api/backend/projects | Creates project, provisions Turso DB, deploys Worker, saves projectId |
projectId present | POST /api/backend/projects/:id/redeploy | Redeploys with existing env vars + Turso credentials preserved |
Deploying a Node.js App
UGA HOST Node.js projects run as Cloudflare Workers — a V8-isolate environment, not a Node.js process. Two coding styles are supported:
Style 1 — Native Cloudflare Worker Recommended
Write a standard export default { fetch(request, env) {} } module. The env object automatically contains all your environment variables (including Turso credentials) as Cloudflare plain-text bindings.
export default {
async fetch(request, env) {
const url = new URL(request.url);
// env.TURSO_DATABASE_URL and env.TURSO_AUTH_TOKEN are injected automatically
if (url.pathname === '/') {
return new Response('Hello from UGA HOST!');
}
return new Response('Not found', { status: 404 });
}
};
Style 2 — Express-compatible (polyfilled)
Standard Express apps also work. The platform injects a require() polyfill that intercepts require('express'), require('cors'), and require('dotenv'). Remove app.listen() — it is stripped automatically by the CLI before upload.
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.json({ status: 'ok' });
});
// DO NOT call app.listen() — stripped by the CLI
// Access env vars via process.env.MY_VAR
env.SOME_VAR (Cloudflare-native) use Style 1. If it uses process.env.SOME_VAR (Node-style) use Style 2 with the Express polyfill.Entry file
The CLI always reads index.js from the current directory. No other entry point is supported for Node.js.
Deploying a Python App
Python projects run as isolated containers managed by the UGA HOST Python Runtime. The container starts on the first request and stays warm as long as it receives traffic.
Entry file
The CLI reads app.py from the current directory.
Standard mode (default) Recommended
Start a regular HTTP server. The platform routes HTTP traffic to the port specified in PORT environment variable.
from flask import Flask, jsonify
import os
app = Flask(__name__)
@app.route('/')
def index():
return jsonify({ 'status': 'ok' })
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ['PORT']))
Supported frameworks
| Framework | Detection pattern |
|---|---|
| Flask | Flask(...) or app.run(...) |
| FastAPI | FastAPI(...) or uvicorn.run(...) |
| Starlette | Starlette(...) |
| Django | Django(...) |
| Bottle | Bottle(...) |
| Standard library | HTTPServer(...) / ThreadingHTTPServer(...) |
Dependencies — requirements.txt
Place a requirements.txt file in the same directory. It is uploaded alongside app.py and installed into the container automatically.
flask==3.0.3
requests==2.32.3
libsql-client==0.3.1
Health check after deploy
After uploading, the CLI calls GET /runtime-health to verify the container started. Cold-start timeouts (503) are non-fatal — the deploy is still considered successful and the container will start on the first real request.
Redeploying
Once ugahost.json contains a projectId, every subsequent ugahost deploy is a redeploy. The platform:
- Reads all saved environment variables from the database
- Re-injects Turso credentials (
TURSO_DATABASE_URL,TURSO_AUTH_TOKEN) from the project record - Deploys the new code with the full env set intact
- Updates
last_deployed_atand caches the code snapshot for future env-change redeploys
ugahost env set persist across all future deploys automatically.Python Pre-deploy Validation
Before any Python code reaches the platform, the CLI runs a local validation pass. If any check fails, the deploy is aborted with a descriptive error message — no code is uploaded.
Check pipeline
python3 -c "import ast; ast.parse(...)". If no local Python is found, this step is skipped silently.PORT environment variable. The platform assigns a port dynamically; hardcoding a port number will break routing.sys.exit() at the top level (kills the server immediately) and import __main__ at the top level (causes infinite loops).from workers import, WorkerEntrypoint, pyodide.http, on_fetch).All validation rules
| Rule | Condition | Result |
|---|---|---|
| Syntax | AST parse fails | ✗ Blocked |
| HTTP server | No Flask/FastAPI/HTTPServer/uvicorn found | ✗ Blocked |
| PORT usage | String PORT not present in code | ✗ Blocked |
| sys.exit() | Top-level sys.exit() call | ✗ Blocked |
| import __main__ | Top-level import __main__ | ✗ Blocked |
| Worker APIs in std mode | from workers import, WorkerEntrypoint, pyodide | ✗ Blocked |
| All pass | — | ✓ Deploy proceeds |
port = int(os.environ.get('PORT', '8080')) is the correct pattern. Never hardcode a port number.Node.js Pre-deploy Processing
Node.js code is not validated as strictly as Python, but the CLI applies two transformations before upload:
1. app.listen() removal
Any app.listen(...) call (including multi-line callback blocks) is stripped from the code. Workers do not have a listening port — the platform handles all routing.
2. Export injection
If the code contains none of export default, module.exports, or globalThis.app, the following is appended automatically:
// Export for UGA HOST
globalThis.app = app;
If your code already has export default { fetch(...) }, no injection occurs and the native Worker entry point is used as-is.
Turso Database — Auto Provisioning
Every project gets a dedicated Turso (libSQL / SQLite) database provisioned automatically on first deploy. You never need to create one manually.
What happens on first deploy
ugahost-{subdomain} in the default group of your organisation.turso_database_url, turso_auth_token) and to backend_env_vars as secrets.env object at runtime.Credentials available in your app
| Variable | Available in | Description |
|---|---|---|
TURSO_DATABASE_URL | env.TURSO_DATABASE_URL (Node.js native)process.env.TURSO_DATABASE_URL (Express)os.environ['TURSO_DATABASE_URL'] (Python) | libSQL URL — libsql://…turso.io |
TURSO_AUTH_TOKEN | Same patterns as above | JWT auth token for Turso HTTP API |
DATABASE_URL | Same patterns as above | Alias for TURSO_DATABASE_URL |
DATABASE_AUTH_TOKEN | Same patterns as above | Alias for TURSO_AUTH_TOKEN |
backend_env_vars and re-injected automatically on every ugahost deploy. You never need to re-enter them.Accessing the Database in Your App
Node.js — native Worker (recommended)
Use the Turso HTTP API directly. The credentials are already on env:
async function query(env, sql, args = []) {
const url = env.TURSO_DATABASE_URL
.replace('libsql://', 'https://') + '/v2/pipeline';
const res = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.TURSO_AUTH_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
requests: [
{ type: 'execute', stmt: { sql, args: args.map(v => ({ type: 'text', value: String(v) })) } },
{ type: 'close' }
]
})
});
const data = await res.json();
return data.results?.[0];
}
Python — using libsql-client
import os, libsql_client
client = libsql_client.create_client_sync(
url=os.environ['TURSO_DATABASE_URL'],
auth_token=os.environ['TURSO_AUTH_TOKEN']
)
rows = client.execute("SELECT * FROM users").rows
Schema initialization pattern
Because Workers and containers cold-start, the recommended pattern is to run CREATE TABLE IF NOT EXISTS on every startup:
async function initDb(env) {
await query(env, `CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)`);
}
Database CLI Commands
The ugahost db subcommand gives you full database access from the terminal. All commands must be run from a directory containing a deployed ugahost.json.
| Command | Description |
|---|---|
ugahost db info | Show DB type, URL, tables and row counts |
ugahost db tables | List all tables with row counts |
ugahost db query "<SQL>" | Run any raw SQL statement |
ugahost db find <table> [where] | SELECT rows, optional JSON where filter |
ugahost db get <table> <id> | SELECT single row by id |
ugahost db insert <table> <json> | INSERT a new row |
ugahost db update <table> <where> <set> | UPDATE rows matching where clause |
ugahost db delete <table> <where> | DELETE rows matching where clause |
ugahost db drop <table> | DROP TABLE (with confirmation prompt) |
ugahost db count <table> [where] | COUNT rows |
ugahost db migrate <file> | Run a .sql or .json migration file against the live database |
ugahost db export <table> [-o file] | Export all rows to a JSON file |
ugahost db import <table> <file> | Bulk-insert rows from a JSON array file |
Quick examples
# Create a table with a single SQL statement
ugahost db query "CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, body TEXT)"
# Insert a row
ugahost db insert posts '{"title":"Hello","body":"World"}'
# SELECT with a filter (JSON key=value)
ugahost db find posts '{"title":"Hello"}'
# Raw SQL with JOIN
ugahost db query "SELECT u.username, COUNT(p.id) as posts FROM users u LEFT JOIN posts p ON p.user_id = u.id GROUP BY u.id"
Migrations — ugahost db migrate
ugahost db migrate <file> runs a script of SQL statements (or a structured JSON migration)
against your live Turso database. Every statement is executed individually, with a real-time
progress line printed for each one — so you can see exactly which step succeeded or failed.
.sql file, commit it to source control, and run it
against any project with one command.
How it works
; — each non-empty segment becomes one statement.
Comments (--) and blank lines are ignored.
/database/query API endpoint.
A spinner shows [1/N] CREATE TABLE … live in the terminal.
Migration complete: 5/5 succeeded
or Migration finished: 4 succeeded, 1 failed.
SQL migration file (.sql) Most common
Create a plain .sql file with one or more statements separated by ;:
-- migrations/001_init.sql
-- Creates the core schema for the auth API
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT UNIQUE,
salt TEXT NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
token TEXT NOT NULL UNIQUE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
expires_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)
Run it:
ugahost db migrate ./migrations/001_init.sql
Terminal output:
🔄 Running migration: 001_init.sql
5 statement(s)
✅ [1/5] CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY…
✅ [2/5] CREATE TABLE IF NOT EXISTS sessions ( id INTEGER PRIMARY K…
✅ [3/5] CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(…
✅ [4/5] CREATE INDEX IF NOT EXISTS idx_users_username ON users(use…
✅ [5/5] CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)
✅ Migration complete: 5/5 succeeded
Seed data in the same file
You can mix DDL (schema) and DML (data) in one file. Statements run in order:
-- migrations/002_seed.sql
INSERT INTO users (username, email, salt, password_hash, role)
VALUES ('admin', 'admin@example.com', 'abc123', 'hashed_value', 'admin');
INSERT INTO users (username, email, salt, password_hash, role)
VALUES ('alice', 'alice@example.com', 'def456', 'hashed_value', 'user');
INSERT INTO users (username, email, salt, password_hash, role)
VALUES ('bob', 'bob@example.com', 'ghi789', 'hashed_value', 'user')
ugahost db migrate ./migrations/002_seed.sql
JSON migration file (.json)
For programmatically generated migrations, use the JSON format. Each entry in operations has a sql field:
{
"version": "003",
"description": "Add posts table",
"operations": [
{ "sql": "CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, body TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE)" },
{ "sql": "CREATE INDEX IF NOT EXISTS idx_posts_user_id ON posts(user_id)" }
]
}
ugahost db migrate ./migrations/003_posts.json
Recommended project layout
my-api/
├── index.js # or app.py
├── ugahost.json
└── migrations/
├── 001_init.sql # schema — tables, indexes
├── 002_seed.sql # initial data
└── 003_posts.sql # subsequent changes
CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS so migration files are safe to re-run without errors. UGA HOST does not track which migrations have already been applied — idempotent SQL is the simplest solution.
BEGIN; … COMMIT; as a single statement for atomic changes.
Import & Export
Export — ugahost db export
Dumps all rows from a table to a local JSON file. Useful for backups or moving data between projects.
# Export to auto-named file: users-export-1234567890.json
ugahost db export users
# Export to a specific file
ugahost db export users -o ./backups/users.json
Import — ugahost db import
Bulk-inserts rows from a JSON array file into a table. The id field is stripped automatically — the database assigns new IDs.
# Import from a previously exported file
ugahost db import users ./backups/users.json
# Import from a handwritten seed file
ugahost db import products ./seeds/products.json
The seed file must be a JSON array of objects whose keys match the table's column names:
[
{ "name": "Widget A", "price": 9.99, "stock": 100 },
{ "name": "Widget B", "price": 14.99, "stock": 50 }
]
db import when you have a JSON dataset to load into an existing table. Use db migrate when you want to write INSERT statements in SQL — both work equally well for seeding.
Environment Variables
Env vars are stored securely in the platform database and injected into your Worker on every deploy. They are available in your code via the normal patterns for each language.
How injection works
When the platform deploys or redeploys your Worker it:
- Reads all rows from
backend_env_varsfor your project - Merges them with Turso credentials from the project record
- Passes the full set as Cloudflare plain_text bindings in the Worker metadata — making them available on
env.VAR_NAME - Also sets them on
globalThis.process.env.VAR_NAME(polyfill, for Express-style apps)
Env Commands
| Command | Description |
|---|---|
ugahost env list | List all environment variable keys (values hidden for secrets) |
ugahost env set KEY VALUE | Set a variable. Prompts interactively if args omitted. |
ugahost env set KEY VALUE --secret | Mark as secret — value is stored encrypted and shown as *** in logs |
ugahost env unset KEY | Remove a variable and redeploy |
Examples
# Set a plain variable
ugahost env set NODE_ENV production
# Set a secret (API key, password, etc.)
ugahost env set STRIPE_SECRET_KEY sk_live_... --secret
# Interactive mode — prompts for key, value, and secret flag
ugahost env set
# Remove a variable
ugahost env unset NODE_ENV
TURSO_DATABASE_URL or TURSO_AUTH_TOKEN manually. These are provisioned and managed automatically. Overwriting them with incorrect values will break your database connection.Logs
All console.log, console.error, and console.warn calls in your app are captured and stored per-project.
# Fetch the last 100 log lines
ugahost logs
# Fetch more lines
ugahost logs --lines 500
Log levels are colour-coded in the terminal:
| Level | Source | Colour |
|---|---|---|
INFO | console.log / console.info | Cyan |
WARN | console.warn | Yellow |
ERROR | console.error | Red |
DEBUG | console.debug | Gray |
Status
ugahost status
Displays project details and current quota usage:
| Field | Description |
|---|---|
| Name | Project display name |
| URL | Live URL — https://subdomain.gss-tec.com |
| Language | nodejs or python |
| Worker | Internal Cloudflare worker script name |
| Status | RUNNING / STOPPED / FAILED |
| Created / Deployed | Timestamps |
| Quota | Apps used/max, storage MB, requests today |
Full CLI Reference
| Command | Description |
|---|---|
ugahost login | Authenticate with your API key |
ugahost init | Initialise a new project in the current directory |
ugahost deploy | First deploy or redeploy the current project |
ugahost status | Show project status and quota |
ugahost logs [--lines N] | Fetch application logs |
ugahost env list | List environment variable keys |
ugahost env set [KEY] [VALUE] [--secret] | Set an environment variable |
ugahost env unset KEY | Remove an environment variable |
ugahost db info | Show database info and table list |
ugahost db tables | List tables with row counts |
ugahost db query "<SQL>" | Run a raw SQL statement |
ugahost db find <table> [where] | SELECT rows with optional filter |
ugahost db get <table> <id> | SELECT single row by id |
ugahost db insert <table> <json> | INSERT a row |
ugahost db update <table> <where> <set> | UPDATE rows |
ugahost db delete <table> <where> | DELETE rows |
ugahost db drop <table> | DROP a table |
ugahost db count <table> [where] | COUNT rows |
ugahost db migrate <file> | Run a SQL or JSON migration file |
ugahost db export <table> [-o file] | Export table rows to JSON |
ugahost db import <table> <file> | Bulk-insert from a JSON file |
UgaFront Frontend Deployment
Deploy static sites and frontend apps to the global edge via the QSSN PaaS dashboard. Connect your GitHub repo and go live in seconds.
Prerequisites
Repository Requirements
| Requirement | Details |
|---|---|
index.html Landing Page | Your repository must contain an index.html file as the main landing page |
| GitHub Repository | Public or private GitHub repository containing your project code |
| Static Assets | CSS, JavaScript, images, and other static files referenced by index.html |
Account Requirements
| Requirement | Details |
|---|---|
| QSSN PaaS Account | Register for a free QSSN PaaS developer account at qssn-cloud-manager.pages.dev |
| GitHub Integration | Connect your GitHub account for repository access during deployment setup |
index.html at the repository root will fail to deploy.Deployment Steps
Ensure your GitHub repository contains an index.html file as the main landing page. Example structure:
my-project/
├── index.html ← required
├── css/style.css
├── js/script.js
└── README.md
In the QSSN PaaS dashboard, click Create Project and connect your GitHub account to access your repositories. Sign in at qssn-cloud-manager.pages.dev/frontend.
Browse your repositories and select the one you want to deploy. The system will automatically detect your project type and suggest build settings.
| Auto-Detected | Supported Project Types |
|---|---|
|
Build command Output directory Framework type |
Static HTML / CSS / JS React, Vue, Angular Node.js apps |
Set your project name, subdomain, and build configuration.
| Setting | Type | Description |
|---|---|---|
| Project Name | Required | Display name shown in the dashboard |
| Subdomain | Required | Your site will be live at yourapp.gss-tec.com |
| Build Command | Required | e.g. npm run build — leave blank for plain static sites |
| Output Directory | Required | e.g. dist or build — leave blank for repo root |
| Environment Variables | Optional | Key/value pairs injected at build time |
| Custom Domain | Optional | Map your own domain (CNAME/A record setup required) |
| Auto-deploy on Push | Optional | Redeploy automatically on every git push to the linked branch |
Click Deploy from Git to launch your application. Real-time build logs and a live preview URL are available immediately after the build completes.
Best Practices
Repository Structure
| Practice | Details |
|---|---|
Use index.html as entry point | Always ensure your main page is named index.html — the platform serves this file first |
| Organize assets with relative paths | Use relative paths for CSS, JS, and images so they resolve correctly on any subdomain |
| Optimize file sizes | Compress images and minify CSS/JS for faster global edge delivery |
Deployment Tips
| Tip | Details |
|---|---|
| Test locally first | Verify your site works locally (npx serve . or a local dev server) before connecting to QSSN PaaS |
| Use meaningful commit messages | Clear commits make it easy to trace which deployment introduced a change via the build history |
| Monitor build logs | Check the real-time build log in the dashboard if a deployment fails — error messages pinpoint the exact step |
main = production), this gives you a zero-touch CI/CD pipeline with no extra tooling.Need Help?
The QSSN PaaS support team is available to help you succeed with your deployment.
| Channel | Details |
|---|---|
| Email support | support@gss-tec.com |
| UGA HOST support | ugahost@gss-tec.com |
About Gaston Software Solutions LLP
| Detail | Info |
|---|---|
| Company | Gaston Software Solutions LLP |
| Registration No. | 80041130611335 |
| Location | Kampala, Uganda |
| Website | www.gss-tec.com |
| +256 755 274 944 | |
| Gaston Software Solutions LLP |