Skip to content

Commit ba46a2a

Browse files
committed
docs: add community guide site, split instructor content, enforce keyboard-only copy
Adds an MkDocs-Material community guide at `docs/` so this repo can be reused beyond Replay 2026. Splits the single instructor README into four audience-specific pages (solo, Instruqt cohort, non-Instruqt cohort, infrastructure). Trims the repo README to attendee-only copy. Corrects VPS sizing guidance after reading the Temporal CLI docs: 2 vCPU / 4 GB is enough for ~200 concurrent workshop connections, and the real ceiling is SQLite's single writer, not CPU. Sweeps every file I own for non-keyboard characters (em-dashes, smart quotes, unicode arrows, middle dots, multiplication signs, inequality signs, ellipses, copyright marks, emoji) and replaces them with ASCII-only equivalents. - `mkdocs.yml`, `pyproject.toml` (new `docs` group), `justfile`, and `.github/workflows/docs.yml` stand up a Material-themed site with a Temporal-purple palette, auto-deployed to GitHub Pages on push to main - `docs/index.md`, `workshop-overview.md`, `architecture.md`, `try-it-locally.md`, `run-with-instruqt.md`, `run-without-instruqt.md`, and `infrastructure.md` cover every audience with cross-links and a five-card audience selector on the landing page - `instructor/README.md` and `instructor/run_server.sh` removed; content migrated into `docs/run-with-instruqt.md` and `docs/infrastructure.md` - `try-it-locally.md` runs the full Tailscale + `temporal-ts-net` stack on a solo dev's machine. Only Aperture is swapped for a direct OpenAI key; everything else matches the real workshop because Tailscale + Temporal is the point, not the AI agent - `infrastructure.md` sizing revised: 2 vCPU / 4 GB for ~200 connections, `--max-connections 500` and `--connection-rate-limit 50` on `temporal-ts-net`, plus a warning callout that the dev server is not for production and that SQLite is the actual throughput ceiling - `README.md` trimmed to attendee-only with a footer link pointing to the community guide for instructors and tinkerers; replaces the ASCII topology with a Mermaid diagram - Keyboard-only sweep: `-` for all dashes, straight quotes for smart quotes, ASCII arrows (`->` `<-`) for unicode arrows, `,` or `|` for middle dots, `+` for the multiplication sign, `>=` / `<=` / `...` spelled out, `(c)` for copyright. Every emoji in `slides/slides.md` and `slides/theme-temporal/` is gone or reworded. Applies across `docs/`, `slides/slides.md`, `slides/theme-temporal/`, `README.md`, `commit-msg.md`, `mkdocs.yml`, `justfile`, and `pyproject.toml` - `.gitignore` adds `site/` and `.mkdocs_cache/` so the built docs output stays out of version control
1 parent c92aa2b commit ba46a2a

29 files changed

Lines changed: 1357 additions & 284 deletions

.github/workflows/docs.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "docs/**"
8+
- "mkdocs.yml"
9+
- "pyproject.toml"
10+
- ".github/workflows/docs.yml"
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: write
15+
16+
jobs:
17+
deploy:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Install uv
25+
uses: astral-sh/setup-uv@v5
26+
with:
27+
enable-cache: true
28+
29+
- name: Set up Python
30+
run: uv python install 3.13
31+
32+
- name: Install docs dependencies
33+
run: uv sync --group docs
34+
35+
- name: Deploy to GitHub Pages
36+
run: uv run --group docs mkdocs gh-deploy --force --clean

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,7 @@ slide-*.jpeg
227227

228228
# Reference material used to design the theme (source PPTX, logo, bg PNGs)
229229
reference/
230+
231+
# MkDocs build output
232+
site/
233+
.mkdocs_cache/

README.md

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,35 @@
22

33
Workshop materials for the joint Tailscale + Temporal session at **Replay 2026**.
44

5-
## What You'll Build
5+
## What you'll build
66

77
A durable AI weather agent powered by Temporal, secured by Tailscale:
88

99
- **Temporal** orchestrates an agentic loop where an LLM autonomously chains tool calls
10-
- **Tailscale** provides zero-config encrypted networking between your machine and the shared infrastructure
11-
- **Aperture** acts as an API gateway, proxying LLM calls with rate limiting and shared key management
12-
13-
```
14-
┌─────────────────┐ Tailscale ┌──────────────────┐
15-
│ Your Instruqt │◄──── Tailnet ─────►│ Temporal Dev │
16-
│ VM (Worker) │ │ Server (VPS) │
17-
│ │ │ temporal-dev:7233│
18-
│ - Python agent │ Tailscale │ temporal-dev:8233│
19-
│ - Go agent │◄──── Tailnet ─────►│ │
20-
│ (stretch) │ ├──────────────────┤
21-
└─────────────────┘ │ Aperture │
22-
│ (API Gateway) │
23-
│ │ │
24-
└───────┼──────────┘
25-
26-
27-
┌──────────────────┐
28-
│ OpenAI API │
29-
│ (shared key) │
30-
└──────────────────┘
10+
- **Tailscale** provides zero-config encrypted networking between your VM and the shared infrastructure
11+
- **Aperture** proxies LLM calls with rate limiting and shared key management. No OpenAI key needed on your VM
12+
13+
```mermaid
14+
flowchart LR
15+
VM[Your Instruqt VM<br/>Python + Go workers]
16+
TS[Temporal Dev Server<br/>temporal-dev:7233 / :8233]
17+
AP[Aperture<br/>API Gateway]
18+
OAI[OpenAI API<br/>shared key]
19+
VM <-. Tailnet .-> TS
20+
VM <-. Tailnet .-> AP
21+
AP --> OAI
3122
```
3223

3324
## Prerequisites
3425

3526
Your Instruqt VM comes pre-configured with everything you need:
27+
3628
- Python 3.13 and `uv`
37-
- Tailscale (connected to the workshop tailnet)
38-
- Workshop code cloned and dependencies installed
29+
- Go 1.26+
30+
- Tailscale, already connected to the workshop tailnet
31+
- Workshop code cloned, dependencies installed
3932

40-
## Quick Start
33+
## Quick start
4134

4235
Verify your environment:
4336

@@ -52,13 +45,13 @@ Then start with [Exercise 1](exercises/01_hello_tailnet/README.md).
5245
| # | Exercise | Time | Description |
5346
|---|----------|------|-------------|
5447
| 1 | [Hello Tailnet](exercises/01_hello_tailnet/README.md) | 15 min | Run a geo-IP workflow on the shared Temporal server via Tailscale |
55-
| 2 | [Explore Tailscale](exercises/02_explore_tailscale/README.md) | 15 min | Discover your network, understand Aperture, explore access control |
48+
| 2 | [Explore Tailscale](exercises/02_explore_tailscale/README.md) | 15 min | Discover your network, understand Aperture, run a Go worker over `tsnet` |
5649
| 3 | [Weather Agent](exercises/03_weather_agent/README.md) | 25 min | Build a durable AI agent with LLM calls routed through Aperture |
5750
| 4 | [Go Agent](exercises/04_go_agent/README.md) | Stretch | Same agent pattern in Go (take-home challenge) |
5851

5952
## Temporal Web UI
6053

61-
Once connected to the tailnet, open the shared Temporal Web UI:
54+
Once your VM is on the tailnet, open the shared Temporal Web UI:
6255

6356
```
6457
http://temporal-dev:8233
@@ -68,12 +61,14 @@ You'll see everyone's workflows running on the shared server.
6861

6962
## Resources
7063

71-
- [temporal-ts-net](https://github.com/temporal-community/temporal-ts-net) Temporal CLI extension for Tailscale
64+
- [temporal-ts-net](https://github.com/temporal-community/temporal-ts-net) - Temporal CLI extension for Tailscale
7265
- [Temporal Python SDK](https://docs.temporal.io/develop/python)
7366
- [Temporal Go SDK](https://docs.temporal.io/develop/go)
74-
- [Tailscale Documentation](https://tailscale.com/kb)
75-
- [Aperture Documentation](https://docs.tailscale.com/aperture)
67+
- [Tailscale docs](https://tailscale.com/kb)
68+
- [Aperture docs](https://docs.tailscale.com/aperture)
69+
70+
---
7671

77-
## Instructor Setup
72+
### Running this workshop yourself?
7873

79-
See [instructor/README.md](instructor/README.md) for VPS and Instruqt configuration.
74+
This repo is also a community asset. If you want to teach the session, run it locally, or remix the stack, see the [**community guide**](https://temporal-community.github.io/workshop-tailscale-replay-2026/) for instructor walkthroughs (with and without Instruqt), infrastructure setup, and architecture deep-dives.

docs/architecture.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Architecture
2+
3+
Three layers, one workshop.
4+
5+
## Topology
6+
7+
```mermaid
8+
flowchart LR
9+
subgraph VM["Attendee VM (Instruqt or local)"]
10+
PY[Python worker]
11+
GO[Go worker]
12+
end
13+
subgraph VPS["Shared VPS"]
14+
TS["Temporal Dev Server<br/>temporal-dev:7233<br/>temporal-dev:8233"]
15+
AP["Aperture<br/>API Gateway"]
16+
end
17+
OAI["OpenAI API<br/>(shared key)"]
18+
VM <-. Tailnet .-> TS
19+
VM <-. Tailnet .-> AP
20+
AP --> OAI
21+
```
22+
23+
Everything between the attendee machine and the shared infrastructure rides on an encrypted Tailscale mesh. There is no public port open on the VPS. If you're not on the tailnet, you can't reach it.
24+
25+
## The four pieces
26+
27+
### Temporal (durability)
28+
29+
A workflow orchestrator. The workshop uses a single `AgentWorkflow` that calls an Activity, feeds the result back to the LLM, and loops until the LLM is satisfied. Every tool invocation is its own Activity with its own retry policy. If the worker dies mid-reasoning, the workflow picks up exactly where it left off.
30+
31+
### Tailscale (networking)
32+
33+
A mesh VPN built on WireGuard. Every workshop VM joins one tailnet and can reach `temporal-dev:7233` and `http://ai` as if they were on a local network, with no port forwarding, no firewall rules, and no VPN concentrator.
34+
35+
### `temporal-ts-net` (the glue)
36+
37+
A Temporal CLI extension that runs the dev server and joins the tailnet via [tsnet](https://pkg.go.dev/tailscale.com/tsnet). Six lines of Go wrap `temporal server start-dev` and put it on the network under a memorable hostname. Installed on the shared VPS; the workshop consumes the hostname it exposes. Source: [temporal-community/temporal-ts-net](https://github.com/temporal-community/temporal-ts-net).
38+
39+
### Aperture (API gateway)
40+
41+
Sits between attendee code and OpenAI. Holds the real API key, forwards requests to `api.openai.com`, and enforces per-identity rate limits using the caller's Tailscale identity. Attendees never see the key, and one person running 500 agents can't burn the whole budget.
42+
43+
## The two agent patterns
44+
45+
### Tool-calling (Exercise 3, Part 1)
46+
47+
```mermaid
48+
flowchart TD
49+
U["User: 'Weather alerts in California?'"]
50+
LLM["LLM via Aperture"]
51+
A["Temporal Activity<br/>get_weather_alerts('CA')<br/>calls NWS API"]
52+
R["'There are 3 active alerts<br/>in California...'"]
53+
U --> LLM
54+
LLM -->|decides to call tool| A
55+
A -->|weather data| LLM
56+
LLM --> R
57+
```
58+
59+
One decision, one tool, one formatted response. Useful on its own (for example, "answer questions about our API docs") and a stepping stone to the loop.
60+
61+
### Agentic loop (Exercise 3, Part 2)
62+
63+
```mermaid
64+
flowchart TD
65+
U["User: 'What's the weather where I am?'"]
66+
subgraph Loop["Agentic Loop, repeats until LLM is done"]
67+
direction LR
68+
P[LLM picks a tool] --> E[Execute Activity]
69+
E --> F[Feed result back to LLM]
70+
F --> P
71+
end
72+
U --> Loop
73+
Loop --> R[Final response]
74+
```
75+
76+
The LLM reasons through multiple steps on its own. The workshop's weather agent chains `get_ip_address`, `get_location_info`, `get_weather_alerts` with no hand-coded flow control; each tool is dynamically dispatched based on what the LLM asks for.
77+
78+
## Aperture in the middle
79+
80+
```mermaid
81+
sequenceDiagram
82+
autonumber
83+
participant VM as Attendee VM
84+
participant AP as Aperture
85+
participant OAI as OpenAI
86+
VM->>AP: POST /v1/responses<br/>(no API key needed)
87+
Note over AP: Identity: your-vm<br/>Rate: 3 / 10 requests
88+
AP->>OAI: POST /v1/responses<br/>Authorization: Bearer sk-real-openai-key
89+
OAI-->>AP: response
90+
AP-->>VM: response
91+
```
92+
93+
Two things to notice:
94+
95+
1. The attendee never has the real key. They POST to `http://ai/v1/responses` with no Authorization header, and Aperture swaps in the real credential on the way out.
96+
2. Aperture uses the **caller's Tailscale identity** as the rate-limit key. No extra auth tokens, no per-attendee secrets to distribute. Whoever the tailnet says you are, that's who Aperture bills.
97+
98+
## Why this stack
99+
100+
Each piece removes a category of operational pain:
101+
102+
- **Temporal** turns "my agent crashed halfway through reasoning" from a lost conversation into a resumed one.
103+
- **Tailscale** deletes VPN and firewall setup from the attendee onboarding path.
104+
- **`temporal-ts-net`** means the shared Temporal server is a hostname, not an IP:port behind a load balancer.
105+
- **Aperture** means one OpenAI key can serve 50 people without any of them seeing it, and without one person draining the budget.
106+
107+
Remove any one of those and the workshop gets substantially harder to run.

docs/index.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Securing AI Applications with Tailscale and Temporal
2+
3+
A hands-on workshop that teaches three ideas by building one thing:
4+
5+
- **Durability** - Temporal orchestrates an agentic loop so the LLM can crash, retry, and resume without losing state.
6+
- **Zero-config networking** - Tailscale connects every machine in the workshop over an encrypted mesh, no VPN concentrator, no firewall rules.
7+
- **Shared-key safety** - Aperture acts as an API gateway so one OpenAI key serves the whole room with per-identity rate limiting.
8+
9+
## Who this site is for
10+
11+
This site is the **community guide**, a reference for anyone who wants to **run the workshop themselves**, whether that's a conference track, an internal training, or a weekend hack with friends.
12+
13+
If you are *attending* a session, the [GitHub README](https://github.com/temporal-community/workshop-tailscale-replay-2026) and the in-environment instructions have everything you need during the workshop itself. You don't need this site.
14+
15+
## Pick a path
16+
17+
<div class="grid cards" markdown>
18+
19+
- **I want to understand what we're building**
20+
21+
---
22+
23+
Read the [workshop overview](workshop-overview.md) and the [architecture](architecture.md) to see the pieces and why they fit together.
24+
25+
- **I want to try it on my own machine**
26+
27+
---
28+
29+
[Try it locally](try-it-locally.md) runs the full Tailscale + Temporal stack on your own machine, on your personal tailnet. Only Aperture is swapped (direct OpenAI key).
30+
31+
- **I'm teaching a cohort via Instruqt**
32+
33+
---
34+
35+
[Running with Instruqt](run-with-instruqt.md) covers VM requirements, track configuration, and day-of checklists.
36+
37+
- **I'm teaching a cohort without Instruqt**
38+
39+
---
40+
41+
[Running without Instruqt](run-without-instruqt.md) covers onboarding attendees onto your tailnet, Aperture alternatives, and the pre-event checklist.
42+
43+
- **I need to stand up the shared infrastructure**
44+
45+
---
46+
47+
[Infrastructure](infrastructure.md) covers the persistent VPS that runs `temporal-ts-net` and the Tailscale and Aperture pieces behind it.
48+
49+
</div>
50+
51+
## Source material
52+
53+
- **Workshop repo** - [temporal-community/workshop-tailscale-replay-2026](https://github.com/temporal-community/workshop-tailscale-replay-2026)
54+
- **`temporal-ts-net`** - [temporal-community/temporal-ts-net](https://github.com/temporal-community/temporal-ts-net)
55+
- **Temporal SDKs** - [Python](https://docs.temporal.io/develop/python), [Go](https://docs.temporal.io/develop/go)
56+
- **Tailscale** - [docs](https://tailscale.com/kb). **Aperture** - [docs](https://docs.tailscale.com/aperture)

0 commit comments

Comments
 (0)