Quickstart - Flask
1. Install
Section titled “1. Install”pip install z4j-flask z4j-rq z4j-rqscheduler# or: pip install z4j-flask z4j-celery z4j-celerybeat2. Mint an agent
Section titled “2. Mint an agent”From the z4j dashboard, Agents, Mint. The response shows the bearer token and the per-project HMAC secret exactly once. Save both.
3. Initialize in your app factory
Section titled “3. Initialize in your app factory”Z4J(app) reads configuration from Flask's app.config (flat Z4J_* keys), an optional Z4J dict, or the environment. It does not take a config dict as a positional argument.
Either set the values on app.config before calling Z4J(app):
from flask import Flaskfrom z4j_flask import Z4J
def create_app(): app = Flask(__name__) # Supply the brain's HTTP(S) base URL. z4j derives /ws/agent itself. app.config["Z4J_BRAIN_URL"] = "https://z4j.example.com" app.config["Z4J_TOKEN"] = "<token from agent mint>" app.config["Z4J_HMAC_SECRET"] = "..." app.config["Z4J_PROJECT_ID"] = "billing-prod"
Z4J(app) return appOr, more commonly, set them as environment variables (Z4J_BRAIN_URL, Z4J_TOKEN, Z4J_HMAC_SECRET, Z4J_PROJECT_ID, Z4J_AGENT_NAME) and just call Z4J(app). The third form is the nested dict:
app.config["Z4J"] = { "brain_url": "...", "token": "...", "hmac_secret": "...", "project_id": "billing-prod",}Z4J(app)The forms are not equal-precedence aliases. Environment variables override
flat app.config["Z4J_*"] keys, and flat keys override the nested Z4J
dictionary. Z4J_HMAC_SECRET is required by the runtime, but Z4J(app) catches
startup exceptions so Flask continues serving without an agent; inspect
z4j.host.flask.extension errors and do not treat a healthy Flask response as
proof that z4j started.
Z4J(app) registers request teardown hooks and starts the agent's background WebSocket task during app init.
4. Verify
Section titled “4. Verify”Run the doctor first. It reads CLI flags and Z4J_* environment variables; it
does not import your Flask app or read app.config:
python -m z4j_flask doctorIf your app uses only app.config, repeat the effective values as CLI flags or
environment variables for this command. Run as the same user your service runs
under (sudo -u www-data ... for gunicorn). The WebSocket probe does not wait
for agent authentication, so all [OK] rows can still accompany a rejected
token; confirm online state and inspect auth warnings too.
Then boot Flask plus your RQ or Celery worker. Processes that use one bearer
token register as worker slots under one agent, not as separate agents. A
Celery worker can auto-bootstrap after importing z4j_celery with the required
credentials in its environment. An RQ worker has no automatic signal: import
and call z4j_rq.register_worker_bootstrap() from a module the worker loads.
Multiple processes
Section titled “Multiple processes”Without preload, each gunicorn or uWSGI worker imports the app after forking
and registers its own (agent_id, worker_id) slot. With gunicorn --preload or
uWSGI's default preload, the master imports the app once and the children
inherit a runtime whose threads are dead. Wire the exported post-fork hook:
def post_fork(server, worker): from z4j_bare import post_fork as z4j_post_fork
z4j_post_fork()For uWSGI, call the same function from an @postfork hook. Do not call it from
Celery prefork pool children.
Z4J_AGENT_NAME is sent as host metadata and appears in the dashboard's
Host column. It does not rename the agent (that name is chosen when the
agent is minted), and concurrent slots sharing a token overwrite the same
agent-level host field on each handshake:
import osimport socketapp.config["Z4J_AGENT_NAME"] = f"web-{socket.gethostname()}-{os.getpid()}"Troubleshooting
Section titled “Troubleshooting”See framework: Flask for the deeper reference.