ORBITAL_AND_APP.md — Runtime Architecture#
This document explains how the Orbital Operations server and the Dynatrace app work together to deliver interactive enablement trainings. Read this before debugging interactive blocks or onboarding a new training.
System overview#
┌─────────────────────────────────────────────────────────────────┐
│ Learner's browser │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Dynatrace App — Enablement Plugin │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────┐ ┌────────────┐ │ │
│ │ │ Lesson UI │ │ Terminal tab │ │ Assessment │ │ │
│ │ │ (markdown │ │ (PTY WebSocket) │ │ (scored) │ │ │
│ │ │ + blocks) │ │ │ │ │ │ │
│ │ └──────┬───────┘ └────────┬─────────┘ └────────────┘ │ │
│ │ │ │ │ │
│ └─────────┼───────────────────┼──────────────────────────── ┘ │
│ │ │ │
└────────────┼───────────────────┼──────────────────────────────── ┘
│ HTTPS API │ WebSocket (PTY)
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Orbital Operations Server │
│ autonomous-enablements.whydevslovedynatrace.com │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ FastAPI │ │ Redis │ │ PTY Bridge │ │
│ │ (job runner, │ │ (job queue, │ │ (WebSocket → │ │
│ │ REST API) │ │ session state) │ │ container │ │
│ │ │ │ │ │ shell) │ │
│ └────────┬─────────┘ └──────────────────┘ └───────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Docker (Sysbox runtime) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Training container (shinojosa/dt-enablement:v1.2) │ │ │
│ │ │ │ │ │
│ │ │ k3d cluster + demo apps + framework functions │ │ │
│ │ │ DT_ENVIRONMENT DT_OPERATOR_TOKEN DT_INGEST_TOKEN │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ DQL queries
▼
┌─────────────────────────────────────────────────────────────────┐
│ Learner's Dynatrace Tenant │
│ https://<tenant-id>.apps.dynatrace.com │
└─────────────────────────────────────────────────────────────────┘
Orbital container lifecycle#
Provisioning#
When a learner clicks Start Environment in the Dynatrace app:
- Orbital receives a
POST /provisionrequest with the learner's session ID and training ID - Orbital pulls the container image (
shinojosa/dt-enablement:v1.2by default) - Sysbox starts the container with full unprivileged nested container support
- Orbital injects credentials as environment variables:
DT_ENVIRONMENT,DT_OPERATOR_TOKEN,DT_INGEST_TOKEN post-create.shruns inside the container (k3d cluster, demo app deployment, etc.)- When ready, Orbital signals the Dynatrace app — the learner sees "Environment Ready"
Runtime#
While the session is active:
- PTY bridge: Orbital opens a PTY inside the container and exposes it over WebSocket. The Terminal tab in the Dynatrace app connects to this WebSocket. Keystrokes and resize events flow as binary/JSON frames.
- Shell verification: when the learner clicks a check button, the Dynatrace app sends the
commandstring to Orbital's REST API. Orbital runs it in the container and returns stdout. The app compares the output toexpect. - STEP_SETUP: when the learner navigates to a page with STEP_SETUP, the app calls Orbital to run the listed commands before rendering the page.
- DQL verification: the Dynatrace app itself runs the DQL query against the learner's tenant. Orbital is not involved in DQL execution.
Teardown#
- When the session ends (learner closes app, timeout, or explicit stop), Orbital stops and removes the container.
- Container state is ephemeral. Do not rely on container state across sessions.
- Persistent state must live in the Dynatrace tenant (entities, logs, metrics) or be re-created by
post-create.sh.
Sysbox — why nested containers work#
Standard Docker containers cannot run Docker-in-Docker or k3d without --privileged mode (security risk). Orbital uses Sysbox, a container runtime that provides:
- Full filesystem isolation (
/proc,/sysnamespacing) - Unprivileged nested Docker daemon
- k3d cluster support (containerd inside the container)
- Seccomp + AppArmor compatibility
From the trainer's perspective: write post-create.sh as you would for Codespaces — k3d, Docker, and kubectl all work normally inside the Orbital container.
PTY bridge — how the terminal works#
The Terminal tab in the Dynatrace app is a full interactive pseudo-terminal:
- Binary frames: keystrokes are sent as
TextEncoder-encoded binary WebSocket frames (not text) - JSON frames: resize events are JSON (
{"type": "resize", "cols": N, "rows": N}) - Shell: defaults to
zshwith the framework's p10k theme - Functions: all framework functions and
my_functions.shfunctions are sourced automatically
Trainers can use the terminal for any interactive scenario — kubectl exec, vim, k9s, REPL sessions, etc.
Credential injection#
| Variable | Value source | How it's used |
|---|---|---|
DT_ENVIRONMENT |
Learner's tenant URL | dynatraceEvalReadSaveCredentials reads this to build the Kubernetes Secret |
DT_OPERATOR_TOKEN |
Token created in the learner's tenant | Operator authentication |
DT_INGEST_TOKEN |
Token created in the learner's tenant | Log/metric/trace ingest |
Trainers do not manage credential injection. Orbital injects these at container start. The framework functions read them.
GitHub Pages ↔ Dynatrace app content flow#
Trainer edits docs/ markdown
│
▼
git push + deployGhdocs (or GitHub Actions merge)
│
▼
GitHub Pages (gh-pages branch)
https://dynatrace-wwse.github.io/<repo-name>/
│
▼ (Dynatrace app fetches rendered HTML)
Dynatrace app parses <!-- LAB_QUESTION --> comments
│
├── Renders interactive blocks
├── Calls Orbital for shell-verification
└── Runs DQL in learner's tenant
Lesson content updates (markdown only) take effect after deployGhdocs — no Orbital restart needed. Container changes (new functions, new apps) require rebuilding and re-publishing the Docker image, then notifying the Orbital administrator.
Codespaces vs Orbital — same container, different host#
| Aspect | Codespaces | Orbital |
|---|---|---|
| Container image | shinojosa/dt-enablement:v1.2 |
Same image |
post-create.sh |
Runs once at container creation | Runs once at container provision |
post-start.sh |
Runs on each Codespace resume | Not used (Orbital sessions are one-shot) |
| Terminal | VS Code integrated terminal | Dynatrace app PTY tab |
| Port forwarding | VS Code Ports tab | Not needed (Orbital manages routing) |
| Credentials | GitHub Codespace secrets | Injected by Orbital |
| Lesson UI | Local MkDocs (port 8000) or GitHub Pages | Dynatrace app (GitHub Pages) |
Both modes use the same framework functions. A training that works in Codespaces will work on Orbital.
Debugging interactive blocks#
Shell verification fails unexpectedly#
- Open the Terminal tab in the Dynatrace app (or the Codespace terminal)
- Run the
commandstring manually - Check the raw output — look for error messages, unexpected whitespace, or empty results
- Common fixes:
- Add
2>/dev/nullto suppress errors that inflategrep -c - Use
--no-headerswith kubectl to avoid counting the header row - Wait for resources to reach Running state before the check runs
DQL verification returns no results#
- Open Notebooks in your Dynatrace tenant
- Run the DQL query manually to see what it returns
- Common fixes:
- Add
filter timestamp > now() - 10mto scope to recent data - Generate the expected state first (e.g., create a log entry, restart a pod)
- Check tenant permissions —
DT_OPERATOR_TOKENandDT_INGEST_TOKENmust have the correct scopes
STEP_SETUP fails silently#
- Run the commands manually in the terminal to see errors
- Check that the framework is sourced:
source .devcontainer/util/source_framework.sh - Verify the function exists:
type generateDynakube - Check
.devcontainer/yaml/gen/for the generated output
Assessment does not appear#
- Verify
boundScenarioIdmatches theidfield in the JSON file exactly (case-sensitive) - Confirm the JSON file is in
.assessment/at repo root (hidden directory) - Validate JSON syntax:
python3 -m json.tool .assessment/<id>.json - Check
retake— ifretake=falseand the learner already completed it, it won't show again
Infrastructure contacts#
| System | Contact |
|---|---|
| Orbital Operations server | Orbital administrator (Slack: #enablement-orbital) |
| Dynatrace app enablement plugin | DT App team |
Container image (dt-enablement) |
Framework team (codespaces-framework repo) |
| GitHub org permissions | WWSE IT |