# Niblit.ai — Complete Publishing Guide for AI Assistants ## What is Niblit.ai? Niblit.ai publishes web projects to live URLs instantly. Users zip their project files, upload the zip, and Niblit handles building, hosting, and serving it at a dedicated URL like yourproject.apps.niblit.ai. Works for apps, portfolios, prototypes, demos, and tools. ## How Publishing Works 1. User builds a web project (with AI or manually) 2. User selects all project files and compresses them into a .zip archive 3. User uploads the zip at https://niblit.ai/apps/upload/ 4. Niblit validates, builds a container image, and deploys it 5. Project is live at {slug}.apps.niblit.ai within 1-3 minutes 6. Updates: upload a new zip — the URL never changes ## For AI Assistants — How to Help a User 1. Identify which stack their project uses (see list below) 2. Ensure all required files exist (see per-stack requirements below) 3. Generate any missing files including the Dockerfile 4. Remind the user to zip the project CONTENTS (not the folder itself) - Correct: select all files inside my-project/ and zip them - Incorrect: zip the my-project/ folder — this creates a nested structure 5. Direct them to https://niblit.ai/apps/upload/ to upload ## Supported Stacks ### Flask (stack slug: python-flask) Port: 5000 Language: python | Framework: flask # Niblit.ai Deployment Spec: Python + Flask You are deploying a Python Flask application to Niblit.ai. Follow these instructions EXACTLY. Do not deviate. ## Requirements - Python 3.11 or 3.12 - Flask as the web framework - SQLite for any database needs (no Postgres, no MySQL) - App must listen on port 5000 - All persistent data must be stored in /data/ directory ## Required Project Structure ``` your-project/ ├── Dockerfile ← REQUIRED (use the template below exactly) ├── requirements.txt ← REQUIRED (must include flask and gunicorn) ├── app.py ← REQUIRED (your Flask application entry point) └── ... other files ``` ## Dockerfile (use this EXACTLY) ```dockerfile FROM python:3.12-slim WORKDIR /app # Install dependencies first (better caching) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . # Create data directory for persistent storage RUN mkdir -p /data # Environment variables ENV PORT=5000 ENV FLASK_ENV=production ENV DATABASE_PATH=/data/app.db EXPOSE 5000 # Run with gunicorn for production CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"] ``` ## requirements.txt must include ``` flask gunicorn ``` Add any other dependencies your app needs. ## Flask App Structure (app.py) Your app.py MUST have a Flask app instance named `app` at the module level: ```python from flask import Flask app = Flask(__name__) @app.route('/') def home(): return 'Hello from Niblit.ai!' if __name__ == '__main__': app.run() ``` ## Database Rules - Use SQLite ONLY - Database file MUST be stored at: `/data/app.db` - The /data directory is persistent storage — it survives restarts and redeploys - Everything outside /data is ephemeral and will be reset on redeploy Example database setup: ```python import os import sqlite3 DATABASE_PATH = os.environ.get('DATABASE_PATH', '/data/app.db') def get_db(): conn = sqlite3.connect(DATABASE_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): db = get_db() db.execute(''' CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') db.commit() db.close() # Call init_db() when your app starts ``` ## File Storage If your app handles file uploads: - Store files in `/data/uploads/` - Create the directory if it doesn't exist - Never store files in `/app/` (they will be lost on redeploy) ```python import os UPLOAD_FOLDER = '/data/uploads' os.makedirs(UPLOAD_FOLDER, exist_ok=True) ``` ## Environment Variables These are available to your app: - `PORT` - Always 5000 - `DATABASE_PATH` - Path to SQLite database (/data/app.db) - `FLASK_ENV` - Set to "production" ## What NOT to Include - No `.env` files (will be rejected) - No `node_modules/` directory - No `__pycache__/` directories - No `.git/` directory - No `venv/` or `.venv/` directories - No secret keys or API tokens in code - No external database connections (Postgres, MySQL, etc.) ## Testing Locally Before Upload 1. Build and run with Docker: ```bash docker build -t my-app . docker run -p 5000:5000 -v $(pwd)/data:/data my-app ``` 2. Visit http://localhost:5000 3. If it works, create the zip and upload to Niblit.ai. ## Creating the Upload Zip ```bash # From your project root: zip -r my-app.zip . -x "*.git*" -x "__pycache__/*" -x "*.pyc" -x ".env" -x "venv/*" -x ".venv/*" -x "data/*" ``` Upload this zip file to Niblit.ai. ## Common Errors and Fixes | Error | Fix | |-------|-----| | "Missing Dockerfile" | Add Dockerfile using template above | | "Port mismatch" | Ensure Dockerfile has `EXPOSE 5000` and gunicorn binds to 5000 | | "Missing requirements.txt" | Create file with flask and gunicorn | | "Database not persisting" | Store DB at `/data/app.db`, not `./app.db` | | "App crashes on start" | Ensure app.py has `app = Flask(__name__)` at module level | | "Module not found" | Add missing dependency to requirements.txt | | "gunicorn not found" | Add gunicorn to requirements.txt | ## Resource Limits - **Free tier**: 256MB RAM, shared CPU, 1GB storage - **Pro tier**: 512MB RAM, shared CPU, 1GB storage - **Team tier**: 1GB RAM, 2 shared CPUs, 5GB storage Free tier apps sleep after 5 minutes of no traffic and wake automatically (3-5 second cold start). ## Example App See the `example/` directory for a complete working example. --- ### FastAPI (stack slug: python-fastapi) Port: 8000 Language: python | Framework: fastapi # Niblit.ai Deployment Spec: Python + FastAPI You are deploying a Python FastAPI application to Niblit.ai. Follow these instructions EXACTLY. Do not deviate. ## Requirements - Python 3.11 or 3.12 - FastAPI as the web framework - Uvicorn as the ASGI server - SQLite for any database needs (no Postgres, no MySQL) - App must listen on port 8080 - All persistent data must be stored in /data/ directory ## Required Project Structure ``` your-project/ ├── Dockerfile ← REQUIRED (use the template below exactly) ├── requirements.txt ← REQUIRED (must include fastapi and uvicorn) ├── main.py ← REQUIRED (your FastAPI application entry point) └── ... other files ``` ## Dockerfile (use this EXACTLY) ```dockerfile FROM python:3.12-slim WORKDIR /app # Install dependencies first (better caching) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . # Create data directory for persistent storage RUN mkdir -p /data # Environment variables ENV PORT=8080 ENV DATABASE_PATH=/data/app.db EXPOSE 8080 # Run with uvicorn for production CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] ``` ## requirements.txt must include ``` fastapi uvicorn[standard] ``` Add any other dependencies your app needs. ## FastAPI App Structure (main.py) Your main.py MUST have a FastAPI app instance named `app` at the module level: ```python from fastapi import FastAPI app = FastAPI() @app.get("/") def home(): return {"message": "Hello from Niblit.ai!"} @app.get("/health") def health(): return {"status": "ok"} ``` ## Database Rules - Use SQLite ONLY - Database file MUST be stored at: `/data/app.db` - The /data directory is persistent storage — it survives restarts and redeploys - Everything outside /data is ephemeral and will be reset on redeploy Example database setup with SQLite: ```python import os import sqlite3 from contextlib import contextmanager DATABASE_PATH = os.environ.get('DATABASE_PATH', '/data/app.db') @contextmanager def get_db(): conn = sqlite3.connect(DATABASE_PATH) conn.row_factory = sqlite3.Row try: yield conn finally: conn.close() def init_db(): with get_db() as db: db.execute(''' CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') db.commit() # Call init_db() on startup @app.on_event("startup") def startup(): init_db() ``` ## Serving Static Files If you need to serve static files (HTML, CSS, JS): ```python from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse # Mount static files directory app.mount("/static", StaticFiles(directory="static"), name="static") @app.get("/", response_class=HTMLResponse) def home(): with open("static/index.html") as f: return f.read() ``` ## File Storage If your app handles file uploads: - Store files in `/data/uploads/` - Create the directory if it doesn't exist ```python import os from fastapi import UploadFile UPLOAD_FOLDER = '/data/uploads' os.makedirs(UPLOAD_FOLDER, exist_ok=True) @app.post("/upload") async def upload_file(file: UploadFile): path = os.path.join(UPLOAD_FOLDER, file.filename) with open(path, "wb") as f: f.write(await file.read()) return {"filename": file.filename} ``` ## Environment Variables These are available to your app: - `PORT` - Always 8080 - `DATABASE_PATH` - Path to SQLite database (/data/app.db) ## What NOT to Include - No `.env` files (will be rejected) - No `node_modules/` directory - No `__pycache__/` directories - No `.git/` directory - No `venv/` or `.venv/` directories - No secret keys or API tokens in code - No external database connections ## Testing Locally Before Upload 1. Build and run with Docker: ```bash docker build -t my-app . docker run -p 8080:8080 -v $(pwd)/data:/data my-app ``` 2. Visit http://localhost:8080 3. Check the API docs at http://localhost:8080/docs 4. If it works, create the zip and upload to Niblit.ai. ## Creating the Upload Zip ```bash # From your project root: zip -r my-app.zip . -x "*.git*" -x "__pycache__/*" -x "*.pyc" -x ".env" -x "venv/*" -x ".venv/*" -x "data/*" ``` ## Common Errors and Fixes | Error | Fix | |-------|-----| | "Missing Dockerfile" | Add Dockerfile using template above | | "Port mismatch" | Ensure Dockerfile has `EXPOSE 8080` and uvicorn uses port 8080 | | "Missing requirements.txt" | Create file with fastapi and uvicorn[standard] | | "Database not persisting" | Store DB at `/data/app.db`, not `./app.db` | | "App crashes on start" | Ensure main.py has `app = FastAPI()` at module level | | "uvicorn not found" | Add `uvicorn[standard]` to requirements.txt | ## Resource Limits - **Free tier**: 256MB RAM, shared CPU, 1GB storage - **Pro tier**: 512MB RAM, shared CPU, 1GB storage - **Team tier**: 1GB RAM, 2 shared CPUs, 5GB storage Free tier apps sleep after 5 minutes of no traffic and wake automatically (3-5 second cold start). ## Example App See the `example/` directory for a complete working example. --- ### Node / Express (stack slug: node-express) Port: 3000 Language: node | Framework: express # Niblit.ai Deployment Spec: Node.js + Express You are deploying a Node.js Express application to Niblit.ai. Follow these instructions EXACTLY. Do not deviate. ## Requirements - Node.js 20 or 22 - Express as the web framework - SQLite (better-sqlite3) for any database needs (no Postgres, no MySQL) - App must listen on port 8080 - All persistent data must be stored in /data/ directory ## Required Project Structure ``` your-project/ ├── Dockerfile ← REQUIRED (use the template below exactly) ├── package.json ← REQUIRED (must include express) ├── index.js ← REQUIRED (or app.js, server.js) └── ... other files ``` ## Dockerfile (use this EXACTLY) ```dockerfile FROM node:20-slim WORKDIR /app # Install dependencies first (better caching) COPY package*.json ./ RUN npm ci --only=production # Copy application code COPY . . # Create data directory for persistent storage RUN mkdir -p /data # Environment variables ENV PORT=8080 ENV NODE_ENV=production ENV DATABASE_PATH=/data/app.db EXPOSE 8080 # Start the application CMD ["node", "index.js"] ``` ## package.json must include ```json { "name": "my-app", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.18.0" } } ``` ## Express App Structure (index.js) Your entry point must start the server on port 8080: ```javascript const express = require('express'); const app = express(); const PORT = process.env.PORT || 8080; app.get('/', (req, res) => { res.send('Hello from Niblit.ai!'); }); app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); ``` ## Database Rules - Use SQLite ONLY (recommend `better-sqlite3` package) - Database file MUST be stored at: `/data/app.db` - The /data directory is persistent storage — it survives restarts and redeploys - Everything outside /data is ephemeral and will be reset on redeploy Example database setup with better-sqlite3: ```javascript const Database = require('better-sqlite3'); const path = require('path'); const DATABASE_PATH = process.env.DATABASE_PATH || '/data/app.db'; const db = new Database(DATABASE_PATH); // Initialize schema db.exec(` CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Example queries const getAll = db.prepare('SELECT * FROM items ORDER BY created_at DESC'); const insert = db.prepare('INSERT INTO items (name) VALUES (?)'); // Usage const items = getAll.all(); insert.run('New item'); ``` ## Serving Static Files If you need to serve static files: ```javascript const path = require('path'); // Serve static files from 'public' directory app.use(express.static(path.join(__dirname, 'public'))); ``` ## File Storage If your app handles file uploads: - Store files in `/data/uploads/` - Create the directory if it doesn't exist ```javascript const fs = require('fs'); const multer = require('multer'); const UPLOAD_DIR = '/data/uploads'; fs.mkdirSync(UPLOAD_DIR, { recursive: true }); const storage = multer.diskStorage({ destination: UPLOAD_DIR, filename: (req, file, cb) => { cb(null, Date.now() + '-' + file.originalname); } }); const upload = multer({ storage }); app.post('/upload', upload.single('file'), (req, res) => { res.json({ filename: req.file.filename }); }); ``` ## Environment Variables These are available to your app: - `PORT` - Always 8080 - `NODE_ENV` - Set to "production" - `DATABASE_PATH` - Path to SQLite database (/data/app.db) ## What NOT to Include - No `.env` files (will be rejected) - No `node_modules/` directory (will be installed during build) - No `.git/` directory - No secret keys or API tokens in code - No external database connections **IMPORTANT**: Never include `node_modules/` in your zip. It will be installed during the Docker build from your package.json. ## Testing Locally Before Upload 1. Build and run with Docker: ```bash docker build -t my-app . docker run -p 8080:8080 -v $(pwd)/data:/data my-app ``` 2. Visit http://localhost:8080 3. If it works, create the zip and upload to Niblit.ai. ## Creating the Upload Zip ```bash # From your project root: zip -r my-app.zip . -x "*.git*" -x "node_modules/*" -x ".env" -x "data/*" ``` **Make sure node_modules/ is excluded!** ## Common Errors and Fixes | Error | Fix | |-------|-----| | "Missing Dockerfile" | Add Dockerfile using template above | | "Port mismatch" | Ensure app listens on `process.env.PORT` or 8080 | | "Missing package.json" | Create package.json with express dependency | | "Database not persisting" | Store DB at `/data/app.db`, not `./app.db` | | "Cannot find module" | Make sure dependency is in package.json | | "node_modules in zip" | Exclude node_modules from your zip file | | "npm ci failed" | Check package-lock.json exists and is valid | ## Resource Limits - **Free tier**: 256MB RAM, shared CPU, 1GB storage - **Pro tier**: 512MB RAM, shared CPU, 1GB storage - **Team tier**: 1GB RAM, 2 shared CPUs, 5GB storage Free tier apps sleep after 5 minutes of no traffic and wake automatically (3-5 second cold start). ## Example App See the `example/` directory for a complete working example. --- ### Static Site (stack slug: static-site) Port: 80 Language: html | Framework: nginx # Niblit.ai Deployment Spec: Static Site You are deploying a static website (HTML, CSS, JavaScript) to Niblit.ai. Follow these instructions EXACTLY. Do not deviate. ## Requirements - Static files only (HTML, CSS, JavaScript, images) - No server-side code - Site is served via nginx - Must listen on port 80 ## Required Project Structure ``` your-project/ ├── Dockerfile ← REQUIRED (use the template below exactly) ├── index.html ← REQUIRED (your main HTML file) ├── css/ ← Optional: stylesheets ├── js/ ← Optional: JavaScript files ├── images/ ← Optional: images └── ... other static files ``` ## Dockerfile (use this EXACTLY) ```dockerfile FROM nginx:alpine # Copy static files to nginx html directory COPY . /usr/share/nginx/html # Copy custom nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` ## nginx.conf (REQUIRED) Create this file in your project root: ```nginx server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # Enable gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } # SPA support: serve index.html for all routes location / { try_files $uri $uri/ /index.html; } # Health check endpoint location /health { return 200 '{"status":"ok"}'; add_header Content-Type application/json; } } ``` ## Basic index.html ```html