Private Modules

The private/ directory is for Python code that performs sensitive operations. Functions here are only reachable through the server layer — the JavaScript frontend has no direct access to them, and they are never exposed as importable modules from the UI side.

What Belongs in private/

  • Database reads and writes (SQLite, PostgreSQL, etc.)
  • File system operations (reading/writing user data files)
  • Cryptographic operations (hashing, encryption, signing)
  • Calls to authenticated or paid external APIs (API keys live here)
  • Credential and token management
  • Any logic that must not be visible or inspectable from the frontend

If a function is purely a helper with no sensitive data access, put it in public/ instead. Keep private/ for operations where exposure would be a security risk.

File Structure

private/
└── secret_processor.py   ← default sensitive operations module

Add additional files as your application grows (e.g. private/database.py, private/auth.py). Each file is imported as from private import database etc.

The Default Module: secret_processor.py

The template ships with private/secret_processor.py containing a demonstration of a secure backend operation:

# private/secret_processor.py
import hashlib
import os

def process_secure_data(data):
    if not data:
        return "No data provided."

    encrypted = hashlib.sha256(data.encode('utf-8')).hexdigest()

    # Example: write to a local database
    # db.execute("INSERT INTO secret_table (data_hash) VALUES (?)", encrypted)

    return f"Private Layer successfully secured the data. SHA-256 Hash: {encrypted[:15]}..."

This function hashes the input string with SHA-256 — a stand-in for any real cryptographic or database operation. The raw input never leaves the Python layer; the UI only receives a confirmation message and a truncated hash.

Importing Private Modules in the Server

Import at the top of server/api.py and call inside the relevant action handler:

from private import secret_processor

def handle_message(message_str):
    ...
    elif action == "private_demo":
        result = secret_processor.process_secure_data(req.get("secret_data", ""))
        return json.dumps({"status": "ok", "result": result})

Only ever import private modules inside server/api.py (or other server-side files). Never import them from public/ or from the UI layer.

Adding a New Private Function

Step 1 — Add the function to an existing or new file inside private/:

# private/database.py
import sqlite3

DB_PATH = "data/app.db"

def save_user_note(user_id, note_text):
    con = sqlite3.connect(DB_PATH)
    con.execute(
        "INSERT INTO notes (user_id, body) VALUES (?, ?)",
        (user_id, note_text)
    )
    con.commit()
    con.close()
    return "Note saved."

Step 2 — Import the new file and add an action handler in server/api.py:

from private import database

elif action == "save_note":
    result = database.save_user_note(
        req.get("user_id"),
        req.get("note")
    )
    return json.dumps({"status": "ok", "result": result})

Step 3 — Call it from JavaScript:

window.invokeBridge({ action: "save_note", user_id: 1, note: "Buy milk" })
    .then(res => console.log(res.result)); // "Note saved."

The database file, SQL queries, and user IDs never touch the JavaScript layer. The frontend only sees the string returned by json.dumps.

Security Model

The separation between public/ and private/ is enforced by convention, not by a sandbox. The guarantee comes from the architecture:

  • JavaScript can only trigger actions that are explicitly handled in server/api.py.
  • The server layer controls exactly what data is returned to the UI — it never forwards raw database rows, file contents, or secret values unless you explicitly do so.
  • Private modules are never imported by UI code or public modules, so their internals stay opaque to the frontend.

When in doubt, return only what the UI strictly needs. For example, instead of returning a full database row, return only the fields the view requires, or a simple success/failure message.