OAuth in one parameter. Let's Encrypt in two lines.
FX.StartHTTPServer(
routes={
'/': dashboard,
'/api/data': get_data,
'/health': ET.JSON(content={'status': 'ok'}),
},
auth=ET.GoogleOAuth(
client_id='...apps.googleusercontent.com',
client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
allowed_emails=['[email protected]'],
public=['/health'],
),
port=8000,
) | run
Every route except /health now requires Google sign-in. Only the listed emails can access. Everything is automatic: login page, CSRF state, code exchange, JWT decoding, HttpOnly secure cookies.
The auth= field is just an entity. It describes what kind
of auth you want. The runtime implements the OAuth2 Authorization Code flow
on your behalf. You never write token-handling code.
Authenticated routes get a req.user field:
@zef_function
def dashboard(req):
email = req.user.email
name = req.user.name
return ET.HTML(content=f'<h1>Hi {name} ({email})</h1>')
auth=ET.GoogleOAuth(
client_id='...',
client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
# no allowed_emails โ anyone with a google account works
public=['/health'],
)
Each domain has its own settings:
FX.StartHTTPServer(
domains={
'admin.example.com': ET.Domain(
routes={'/': admin_dash},
auth=ET.GoogleOAuth(client_id='...', allowed_emails=['alice@x']),
),
'public.example.com': ET.Domain(
routes={'/': landing},
# no auth โ open to everyone
),
},
port=443,
certificates=[...],
) | run
auth=ET.GitHubOAuth(
client_id='...',
client_secret='...',
allowed_users=['alice', 'bob'], # github usernames
public=['/health'],
)
Getting a cert:
creds = FX.GetLetsEncryptCreds(
domain_name='example.com',
email_address='[email protected]',
) | run
# returns ET.TLSCredentials(domain_name, private_key, certificates)
Let's Encrypt uses HTTP-01 validation โ it needs to reach you on port 80. Stop any other web server first.
FX.SaveToLocalFile(
content=Array([creds]),
path='/etc/zef/certs.zef',
overwrite_existing=True,
) | run
# load later
certificates = list(FX.LoadFromLocalFile(path='/etc/zef/certs.zef') | run)
FX.StartHTTPServer(
domains={
'example.com': ET.Domain(
routes={
'/': ET.HTML(content='<h1>Hello</h1>'),
'/api': api_handler,
},
auth=ET.GoogleOAuth(
client_id='...',
client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
public=['/health'],
),
),
},
port=443,
certificates=certificates,
allow_external_requests=True,
) | run
import os
from zef import *
CERT_PATH = '/etc/zef/certs.zef'
# load or obtain certs
if os.path.exists(CERT_PATH):
certs = list(FX.LoadFromLocalFile(path=CERT_PATH) | run)
else:
creds = FX.GetLetsEncryptCreds(
domain_name='app.example.com',
email_address='[email protected]',
) | run
FX.SaveToLocalFile(
content=Array([creds]),
path=CERT_PATH,
overwrite_existing=True,
) | run
certs = [creds]
@zef_function
def dashboard(req):
return ET.HTML(content=f'<h1>Hi {req.user.name}</h1>')
FX.StartHTTPServer(
domains={
'app.example.com': ET.Domain(
routes={
'/': dashboard,
'/health': ET.JSON(content={'status': 'ok'}),
},
auth=ET.GoogleOAuth(
client_id=os.environ['GOOGLE_CLIENT_ID'],
client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
allowed_emails=['[email protected]', '[email protected]'],
public=['/health'],
),
),
},
port=443,
certificates=certs,
allow_external_requests=True,
) | run
import time
while True:
time.sleep(60)
FX.Log with a subscriber writing to diskFX.SetRole(role='user_python_process')In a typical Flask + gunicorn + nginx + Let's-Encrypt setup you'd:
In Zef: one file, one function call. The runtime does all six.
ET.TLSCredentials(
domain_name='custom.example.com',
private_key=Bytes(open('key.pem', 'rb').read()),
certificates=[Bytes(open('cert.pem', 'rb').read())],
)
Any cert-as-bytes source works. Let's Encrypt is the turnkey option.
You need a single server that serves your dashboard on dashboard.example.com (OAuth-protected for alice and bob) AND a public API on api.example.com with a /health endpoint. Sketch the call.
FX.StartHTTPServer(
domains={
'dashboard.example.com': ET.Domain(
routes={'/': dashboard},
auth=ET.GoogleOAuth(client_id='...', client_secret='...',
allowed_emails=['alice@x', 'bob@y']),
),
'api.example.com': ET.Domain(
routes={'/health': ET.JSON(content={'status': 'ok'}),
'/data': api_handler},
),
},
port=443,
certificates=certs,
allow_external_requests=True,
) | run
Next up: parsing with patterns โ build a JSON parser in 25 lines. โ