From d610cac858edc5050edb7cf76d5cdec9baffbe62 Mon Sep 17 00:00:00 2001 From: Sijie Guo Date: Tue, 6 Oct 2026 10:27:48 -0700 Subject: [PATCH 1/2] Add a team-card Lab 0 to the Cloud course The hackathon environments are now created for each team and handed over as a team card, so participants need a Lab 0 that creates nothing and needs no snctl. This adds one beside the existing Lab 0, which is unchanged. - labs/cloud/00-set-up-team-card.md: fill in .env from the card, load the login stream once for the team, run the doctor. It keeps the step numbers of 00-set-up.md, so every "Lab 0, step 2/3" reference holds for both pages. - The READMEs, .env.cloud.example and docs/before-you-arrive.md point at it. Before-you-arrive tells card holders to skip creating resources. - The tutor asks whether the learner has a team card before Lab 0 of the Cloud course, and uses the matching page. - check-labs.sh accepts a lab page the tutor names with its course folder, and checks that it exists there. - What was run: the new page against a test instance on StreamNative Cloud, on the Python path, plus the doctor and the seeder on the TypeScript path and with the CLI commands. --- .env.cloud.example | 2 + README.md | 4 +- docs/before-you-arrive.md | 10 +- docs/tutor.md | 3 +- labs/README.md | 2 +- labs/cloud/00-set-up-team-card.md | 275 ++++++++++++++++++++++++++++++ labs/cloud/README.md | 19 ++- scripts/check-labs.sh | 8 +- scripts/tests/run.sh | 15 ++ skills/data-agent-tutor/SKILL.md | 16 +- 10 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 labs/cloud/00-set-up-team-card.md diff --git a/.env.cloud.example b/.env.cloud.example index e2c2463..7052acb 100644 --- a/.env.cloud.example +++ b/.env.cloud.example @@ -4,6 +4,8 @@ # cp .env.cloud.example .env # # labs/cloud/00-set-up.md, step 2, has the snctl command for each address. +# With a team card, every value comes from the card instead: +# labs/cloud/00-set-up-team-card.md, step 2. # (The Local course writes its own .env: see labs/local/00-set-up.md.) # -------------------------------------------------- from the organizers -- diff --git a/README.md b/README.md index be52d1f..c18cdef 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ The same five labs, on two stacks. | | [Cloud course](labs/cloud/README.md) | [Local course](labs/local/README.md) | |---|---|---| | Runs on | StreamNative Cloud: your own instance, with a Kafka cluster, a SQL workspace, and an agent workspace | Your laptop: [Ursa for Kafka](https://openlakestream.org/docs/ursa-for-kafka), [RisingWave](https://risingwave.com), and the Orca Agent Engine (`ork local`) | -| You need | A StreamNative Cloud login with your own instance, from the hackathon organizers | Docker and an Anthropic API key | +| You need | A StreamNative Cloud login from the hackathon organizers, with a team card or an instance of your own | Docker and an Anthropic API key | | Time | About 40 minutes | About 45 minutes, plus image downloads | -| Start | [Lab 0: Set up](labs/cloud/00-set-up.md) | [Lab 0: Set up](labs/local/00-set-up.md) | +| Start | [Lab 0: Set up](labs/cloud/00-set-up.md), or [from a team card](labs/cloud/00-set-up-team-card.md) | [Lab 0: Set up](labs/local/00-set-up.md) | At the hackathon, take the Cloud course: see [Before you arrive](docs/before-you-arrive.md). Without a StreamNative Cloud diff --git a/docs/before-you-arrive.md b/docs/before-you-arrive.md index cd9deed..7fc92a3 100644 --- a/docs/before-you-arrive.md +++ b/docs/before-you-arrive.md @@ -30,7 +30,8 @@ Everyone also needs: - [`jq`](https://jqlang.org/download/), for the checks in every lab. - [`snctl`](https://docs.streamnative.io/tools/cli/snctl/snctl-overview) (the StreamNative Cloud CLI): `brew install streamnative/streamnative/snctl`. Lab 0 - uses it to read your instance's addresses and to create your topic. + uses it to read your instance's addresses and to create your topic. With a + team card (step 4) you do not need it. ## 1. Get the code @@ -73,6 +74,13 @@ Every line should say `PASS`. ## 4. Set up your instance +**Getting a team card?** If the organizers told you that your team's +environment is created for you, skip this step and create nothing in the +console: your Kafka cluster, SQL workspace, and agent workspace already exist. +Your team card has their addresses and an API key, and +[Lab 0: Set up from a team card](../labs/cloud/00-set-up-team-card.md) starts +from it. + The organizers add you to the hackathon organization on StreamNative Cloud, give you an **instance** of your own, and make a **service account** in it. They give you its name and its **API key**: keep the key to yourself. diff --git a/docs/tutor.md b/docs/tutor.md index 58b6693..0524593 100644 --- a/docs/tutor.md +++ b/docs/tutor.md @@ -49,7 +49,7 @@ last row works in any agent that can read a file. Started with no request, the tutor asks three things: ```text -1. Course: Cloud (your own instance on StreamNative Cloud) or Local (everything on your laptop)? +1. Course: Cloud (on StreamNative Cloud, with a team card from the organizers or with your own instance: say which) or Local (everything on your laptop)? 2. Path: CLI, Python, or TypeScript? 3. What now: start at Lab 0, resume at a lab, quiz me on a lab, or check my setup? ``` @@ -59,6 +59,7 @@ Started with no request, the tutor asks three things: | You want to | Say | |---|---| | Take a course from the start | `Start the Cloud course on the Python path.` | +| Start from a team card | `Start the Cloud course on the Python path. I have a team card.` | | Pick up where you stopped | `Resume the Local course at Lab 3. I'm on TypeScript.` | | Be quizzed | `Quiz me on Lab 2.` | | Find out why something fails | `Check my setup.` Or paste the error. | diff --git a/labs/README.md b/labs/README.md index e6e4cbd..d9fe406 100644 --- a/labs/README.md +++ b/labs/README.md @@ -5,7 +5,7 @@ Two courses teach the same five labs on two stacks. Pick one. | | [Cloud course](cloud/README.md) | [Local course](local/README.md) | |---|---|---| | Runs on | StreamNative Cloud: your own instance, with a Kafka cluster, a SQL workspace, and an agent workspace | Your laptop: Ursa for Kafka, RisingWave, and the Orca Agent Engine | -| You need | A StreamNative Cloud login with your own instance, from the hackathon organizers | Docker and an Anthropic API key | +| You need | A StreamNative Cloud login from the hackathon organizers, with a team card or an instance of your own | Docker and an Anthropic API key | | Time | About 40 minutes | About 45 minutes, plus the image downloads | | Take it when | You are at the event | You have no StreamNative Cloud instance, or you want to see every part run | diff --git a/labs/cloud/00-set-up-team-card.md b/labs/cloud/00-set-up-team-card.md new file mode 100644 index 0000000..547e7fa --- /dev/null +++ b/labs/cloud/00-set-up-team-card.md @@ -0,0 +1,275 @@ +# Lab 0: Set up from a team card + +**Cloud course** · 5 minutes, plus 5 on your own · CLI, Python, or TypeScript + +You put your team card in `.env`, load the login stream into your team's Kafka +cluster, and run the doctor. When this lab is done, the Agent Engine, Kafka, +and Schema Registry on your card all answer, and your topic holds 246 logins. + +No team card? [Lab 0: Set up](00-set-up.md) starts from an instance of your own +instead. Both end in the same place, and Lab 1 is the same after either. + +## Before you start + +- You have your **team card** from the organizers: an **API key**, and the + addresses of the environment they created for your team on StreamNative + Cloud. That environment is a Kafka cluster, a SQL workspace that imports it, + an agent workspace, and the service account the key belongs to. Everything on + the card already exists: you create nothing, and you do not need `snctl`. +- You can log in to StreamNative Cloud, and the organizers added your login to + your team's environment. This lab does not use that login; Labs 2 and 3 do. +- You cloned this repository and opened a terminal in it. The terminal runs + `bash`: on Windows that is WSL or Git Bash, on every path, because the checks + are `bash` commands. +- You have `git`, [`ork`](https://github.com/orca-ae/orca-cli) v0.6.0 or newer, + and [`jq`](https://jqlang.org/download/). `ork` does the first MCP login in + Lab 3 for all three paths, and `ork` with `jq` runs the checks in every lab. + +## Step 1: Install your path + +Pick **one** path. Your teammate can pick a different one. + +**Python** (3.11 or newer) + +```bash +cd python +python3 -m venv .venv +source .venv/bin/activate # Git Bash on Windows: source .venv/Scripts/activate +pip install -r requirements.txt +``` + +**TypeScript** (Node.js 20 or newer) + +```bash +cd typescript +npm install +``` + +**CLI**: `ork` and `jq` are all the labs need. Set up Python or TypeScript as +above too: the doctor, the seeder, and the data injector come from one of them. + +### Check + +Run the doctor without the network. Every line says `PASS`. + +| Python or CLI | TypeScript | +|---|---| +| `python doctor.py --offline` | `npm run doctor -- --offline` | + +```text +PASS Python 3.11+ 3.13 +PASS package runorca +PASS package confluent-kafka[avro] +PASS package python-dotenv +PASS ork found +PASS jq found + +All good: you're ready. +``` + +## Step 2: Fill in `.env` from your team card + +Open a second terminal at the repository root and copy the template: + +```bash +cp .env.cloud.example .env +``` + +`.env` is git-ignored. It will hold your team's key: do not commit it or paste +it anywhere. Give each of these lines its value from the card: + +| `.env` line | On your card | Write it as | +|---|---|---| +| `SN_API_KEY` | API key | the raw key, with no `token:` in front | +| `SN_SERVICE_ACCOUNT` | Service account | `@.auth.streamnative.cloud`. If the card has only the name, add the rest, with the organization id from the card (`o-...`) | +| `ORCA_BASE_URL` | Agent workspace endpoint | `https://` and the host, with no `/v1` | +| `KAFKA_BOOTSTRAP_SERVERS` | Broker URL | the host and its port, `:9093` | +| `SCHEMA_REGISTRY_URL` | Schema registry URL | `https://` and the host | +| `SN_MCP_URL` | SQL workspace MCP endpoint | `https://mcp.streamnative.cloud/mcp/x//sqlworkspace.compute.streamnative.io/` | +| `SN_SQL_DATABASE` | SQL database | as given. It is the name of your SQL catalog, not of your SQL workspace | + +If your card already is a list of `NAME=value` lines, paste each one over the +empty line with the same name. + +`SN_SQL_DATABASE` is the database you use in Lab 2 and the agent targets in +Labs 3 and 4. Leave the other lines as they are: the MCP server uses a separate +browser login in Lab 3, so `SN_MCP_AUTH=oauth` stays, and `SN_MCP_OAUTH_ISSUER` +stays empty. + +**Two people share one team card.** Your teammate fills in the same values, and +you both work in the same Kafka cluster and the same SQL database. Your agents +stay apart: each is named after its owner's OS user name, like +`hello-agent-ana`. If the two of you have the same user name, each set +`PARTICIPANT` in `.env` to a name of your own. In Lab 2 the view and the table +are created once for the team: if your teammate got there first, the `CREATE` +statement says they already exist, and you go on to the step's check. The reset +script that Lab 4 mentions drops them for both of you, so agree before one of +you runs it. + +### Check + +One authenticated read of your Agent Engine. It prints `true` when the endpoint +and the key on your card are accepted. + +```bash +./lab-ork agent list -o json | jq -e 'has("data")' +``` + +Before you filled in `.env`, the same command says +`Missing ORCA_BASE_URL, SN_API_KEY` instead: the two values it needs. + +## Step 3: Load the login stream + +The course reads one topic in your team's Kafka cluster, +`security.login_events`. It needs the login stream once, for the whole team. +**One of you** runs the seeder, in your path's folder: + +| Python | TypeScript | CLI | +|---|---|---| +| `python seed.py` | `npm run seed` | `(cd ../python && .venv/bin/python seed.py)` or `(cd ../typescript && npm run seed)` | + +It prints one of two lines, and both are fine. A topic that was empty is now +loaded: + +```text +Loaded 246 logins for 91 accounts into security.login_events. +``` + +A topic that the organizers or your teammate loaded before you is left as it is: + +```text +security.login_events already holds 246 events, so it is seeded. To load another copy anyway: python seed.py --force +``` + +Do not use `--force`: a second copy would double every count in Lab 2. The +number is higher than 246 once someone on your team has run Lab 3, and the +TypeScript seeder ends the line with `npm run seed -- --force`. + +The seeder replays [`data/login_events.jsonl`](../../data/login_events.jsonl): +synthetic logins at a fictional bank, with their timestamps moved to now. It +also registers the topic's Avro schema, which SQL Workspace needs in Lab 2. + +If it says `The topic security.login_events does not exist yet`, the topic was +not created with your environment. Raise your hand, or create it yourself with +`snctl`: [Lab 0: Set up](00-set-up.md), step 3. + +### Check + +Run the doctor. It checks your laptop, then each service on your card, and a +failed check prints its fix on the next line. + +| Python | TypeScript | CLI | +|---|---|---| +| `python doctor.py` | `npm run doctor` | `(cd ../python && .venv/bin/python doctor.py)` or `(cd ../typescript && npm run doctor)` | + +After the lines from step 1, it prints: + +```text +PASS .env cloud stack, participant: ana +PASS ORCA_BASE_URL https://... +PASS Agent Engine API key accepted +PASS Kafka security.login_events has 1 partition(s) +PASS Schema Registry security.login_events-value v1 +PASS login topic schema has account_id, event_time, ip_address, result, failure_reason +WAIT MCP OAuth no tutorial vault yet + next: Nothing to do now: Lab 3 opens your browser to authorize the MCP server. Run doctor again after it. + +You're ready. 1 check(s) wait for a later lab. +``` + +`WAIT` is not a failure. The MCP server needs a login that only Lab 3 can do. +On a topic that nobody has loaded yet, the `Schema Registry` line fails before +this step: the seeder is what registers the schema. + +Still failing after two tries? Raise your hand, or see +[Troubleshooting](troubleshooting.md). + +## Check your understanding + +**1. The doctor prints `WAIT MCP OAuth`. What should you do?** + +- A. Fix it now: the doctor has to print only `PASS`. +- B. Nothing yet: Lab 3 does the browser login this check waits for. +- C. Ask for a new team card. + +
+Answer + +**B.** The MCP server wants an OAuth login, and the lab script does it the first +time the agent needs the server. `WAIT` means "not ready, and not your mistake". +A real problem prints `FAIL` and its fix. + +
+ +**2. The seeder says `security.login_events already holds 246 events, so it is seeded`. What should you do?** + +- A. Run it again with `--force`, so that your copy is loaded too. +- B. Nothing: the stream is there already, loaded by the organizers or by your teammate. +- C. Ask for a new Kafka cluster. + +
+Answer + +**B.** Your team shares one topic, and it needs the 246 logins once. A second +copy would double every count in Lab 2. + +
+ +**3. What does `./lab-ork` add to `ork`?** + +- A. It is a different CLI with its own commands. +- B. It points `ork` at your Agent Engine with the key from `.env`, and fills in the ids your scripts saved. +- C. It runs the lab's steps for you. + +
+Answer + +**B.** Everything after `./lab-ork` goes to `ork` as you typed it. The wrapper +only supplies the endpoint, the credential, and the four `@..._id` words. + +
+ +## Try it yourself + +Make the doctor fail on purpose, so you know what a failure looks like before a +real one. Change one value in `.env` so that a check fails, read the fix the +doctor prints, then put the value back. + +### Check + +The doctor ends on the "ready" line again. + +```bash +(cd python && .venv/bin/python doctor.py) | tail -n 1 +``` + +On the TypeScript path, use `npm --prefix typescript run doctor | tail -n 1`. + +
+Solution + +Add `/v1` to the end of `ORCA_BASE_URL` and run the doctor: + +```text +FAIL ORCA_BASE_URL https:///v1 + fix: Use the host root only: ORCA_BASE_URL=https:// +``` + +The doctor does not call an endpoint it can see is wrong. It skips the Agent +Engine check, tells you the exact value to use, and ends with +`1 check(s) failed`. Remove the `/v1` and run it again. + +
+ +## Recap + +- `.env` holds your team card: the addresses of your team's environment and its + key. It is git-ignored. +- Your team shares that environment. The login stream is loaded once, and the + seeder refuses a second copy. +- The doctor checks each service on the card and prints the fix for a failure. +- `./lab-ork` is how you look at your Agent Engine from the terminal. + +## What's next + +[Lab 1: Hello, agent](01-hello-agent.md) diff --git a/labs/cloud/README.md b/labs/cloud/README.md index bbe4b05..fbd3129 100644 --- a/labs/cloud/README.md +++ b/labs/cloud/README.md @@ -25,6 +25,7 @@ flowchart LR | Lab | Time | Where | You | The idea | |---|---|---|---|---| | [0. Set up](00-set-up.md) | 10 min | terminal | Fill in `.env` from your instance, load the topic, run the doctor | Check service access before you build on it | +| or [0. Set up from a team card](00-set-up-team-card.md) | 5 min | terminal | Fill in `.env` from your team card, load the topic, run the doctor | The same, when the organizers created your environment | | [1. Hello, agent](01-hello-agent.md) | 5 min | CLI / Python / TS | Create an agent and chat | Agent, environment, session, events | | [2. Hello, streaming SQL](02-streaming-sql.md) | 8 min | SQL Workspace | Build a materialized view over the topic | Context that keeps itself fresh | | [3. Agent + live context](03-live-context.md) | 9 min | CLI / Python / TS | Give the agent SQL tools, inject new data | The answer changes with the data | @@ -43,8 +44,13 @@ your own. - One path installed, plus `ork`, `jq`, and `snctl`. All of this is in [Before you arrive](../../docs/before-you-arrive.md). -Start with [Lab 0: Set up](00-set-up.md). If something goes wrong, see -[Troubleshooting](troubleshooting.md). How labs and checks work is in +**Have a team card?** Then the organizers created all of this for your team, +and the card has its addresses and an API key. You need only your login, one +path, `ork`, and `jq`, and you start with +[Lab 0: Set up from a team card](00-set-up-team-card.md). + +Otherwise, start with [Lab 0: Set up](00-set-up.md). If something goes wrong, +see [Troubleshooting](troubleshooting.md). How labs and checks work is in [The labs](../README.md), and a coding agent can [tutor you through the course](../../docs/tutor.md). @@ -60,6 +66,15 @@ cluster was Serverless; the SQL workspace ran RisingWave 3.1.0-alpha. - **Lab 0**: every `snctl` lookup, the topic, the seeder, and the doctor, on the Python path. On the TypeScript path, the doctor, and the seeder against the topic once it was loaded. +- **Lab 0 from a team card**: added on 6 October 2026 and run that day against + the same test instance, from a card of ready-made `NAME=value` lines. On the + Python path: every step and check, and the failing doctor run its solution + shows. On the TypeScript path, and with the CLI column's commands: the doctor + and the seeder. The topic was loaded already (274 events, after earlier Lab 3 + runs), so the seeder printed its "already holds" line each time. The card had + no `SN_SQL_DATABASE` line; nothing in Lab 0 reads that value. Not run from + this page: the seeder on an empty topic, a card of labeled values, and an + environment the organizers created. - **Lab 2**: every statement and check, through `psql`. The console was not used. - **Labs 1, 3 and 4**: every step and check, on all three paths, with the model diff --git a/scripts/check-labs.sh b/scripts/check-labs.sh index f1270f6..91bb9f8 100755 --- a/scripts/check-labs.sh +++ b/scripts/check-labs.sh @@ -18,7 +18,8 @@ # ## What's next # # Relative links are checked in README.md, labs/, docs/ and skills/. The lab -# pages the tutor skill names have to exist in both courses. +# pages the tutor skill names have to exist in both courses; a page it names +# with its course folder (labs/cloud/), in that course. # Prints one line per problem and exits 1 if there is any. set -euo pipefail @@ -135,6 +136,11 @@ if [ -f "$skill" ]; then printf '%s names %s, but labs/%s/%s does not exist\n' "$skill" "$page" "$course" "$page" >>"$problems" done done < <(grep -o '`[0-9a-z-]*\.md`' "$skill" | tr -d '`' | sort -u) + # A page it names with its course folder is in that course only. + # shellcheck disable=SC2016 # the backticks are Markdown, not a command + while IFS= read -r page; do + [ -f "$page" ] || printf '%s names %s, but it does not exist\n' "$skill" "$page" >>"$problems" + done < <(grep -o '`labs/[a-z]*/[0-9a-z-]*\.md`' "$skill" | tr -d '`' | sort -u) fi if [ -s "$problems" ]; then diff --git a/scripts/tests/run.sh b/scripts/tests/run.sh index 333006d..f6b4312 100755 --- a/scripts/tests/run.sh +++ b/scripts/tests/run.sh @@ -267,6 +267,21 @@ test_the_tutor_skill_names_only_lab_pages_that_exist() { check "it passes once both courses have every page the tutor names" [ "$STATUS" -eq 0 ] } +test_the_tutor_skill_can_name_a_page_only_one_course_has() { + fresh_root tutor-one-course + two_labs + mkdir -p "$R/labs/local" "$R/skills/data-agent-tutor" + cp "$R"/labs/cloud/*.md "$R/labs/local/" + # shellcheck disable=SC2016 # the backticks are Markdown, not a command + printf 'Both courses: `00-set-up.md`. Cloud only: `labs/cloud/00-set-up-team-card.md`.\n' >"$R/skills/data-agent-tutor/SKILL.md" + lint + check "a page the tutor names with its course folder fails while it is missing" [ "$STATUS" -eq 1 ] + check "naming the skill and the page" says "skills/data-agent-tutor/SKILL.md names labs/cloud/00-set-up-team-card.md" + lab 00-set-up-team-card.md 's/^# Lab 1: Hello, agent/# Lab 0: Set up from a team card/' + lint + check "it passes once that course has the page; the other course does not need one" [ "$STATUS" -eq 0 ] +} + for t in $(declare -F | awk '{print $3}' | grep '^test_'); do "$t"; done printf '\n%d passed, %d failed\n' "$PASSED" "$FAILED" diff --git a/skills/data-agent-tutor/SKILL.md b/skills/data-agent-tutor/SKILL.md index d1b097c..9c11361 100644 --- a/skills/data-agent-tutor/SKILL.md +++ b/skills/data-agent-tutor/SKILL.md @@ -42,15 +42,27 @@ pages, in `labs/cloud/` and in `labs/local/`: `00-set-up.md` · `01-hello-agent.md` · `02-streaming-sql.md` · `03-live-context.md` · `04-act-with-approval.md` · `troubleshooting.md` +**Lab 0 of the Cloud course has two pages.** Which one is theirs depends on one +thing: a **team card**, the addresses of an environment the organizers created +for their team, with an API key. + +- **They have a team card**: `labs/cloud/00-set-up-team-card.md`. +- **They have none**, and look up their own instance with `snctl`: `00-set-up.md`. +- **They have not said**: ask only this, and wait: "Did the organizers give + your team a team card?" + +Labs 1 to 4 are the same pages after either one. + - **They named no course, or no lab and no task**: reply with only this menu. - 1. Course: **Cloud** (your own instance on StreamNative Cloud) or **Local** (everything on your laptop)? + 1. Course: **Cloud** (on StreamNative Cloud, with a team card from the organizers or with your own instance: say which) or **Local** (everything on your laptop)? 2. Path: **CLI**, **Python**, or **TypeScript**? 3. What now: **start** at Lab 0, **resume** at a lab, **quiz me** on a lab, or **check my setup**? - **They pasted an error or a `FAIL` line**: give the fix the troubleshooting page has for that symptom, as a step message. - **They named a lab**: send its first step now. Do not ask them to confirm what they already said. Ask for their path only when the step you are about - to send has one column per path and they have not named theirs. + to send has one column per path and they have not named theirs. Lab 0 of the + Cloud course comes after the team card question, when they have not said. If there is no `labs/` folder in the working directory or above it, say so, ask them to open you in their clone of the repository, and stop. Never teach a lab From e7d74757b254e9814065711f67a6e36e48e85b5e Mon Sep 17 00:00:00 2001 From: Sijie Guo Date: Tue, 6 Oct 2026 10:51:23 -0700 Subject: [PATCH 2/2] Document building the team card from the environment sheet Teams are given access to the environment sheet that the organizers keep and build their team card themselves, so the team-card Lab 0 now says how. - Step 2 maps six columns of a team row to .env lines, gives the console steps for creating an API key for the team service account, and shows a finished card with placeholders. - The READMEs, .env.cloud.example and docs/before-you-arrive.md describe a team environment and its sheet instead of a card that is handed over. - Troubleshooting: with a team card, a rejected key is replaced by creating a new one. - The tutor asks whether the team has a row in the environment sheet. - What was run: steps 2 and 3 against a test instance with the seven lines filled in. The API key steps follow the labels of the console and were not clicked through. --- .env.cloud.example | 3 +- README.md | 2 +- docs/before-you-arrive.md | 16 ++-- docs/tutor.md | 4 +- labs/README.md | 2 +- labs/cloud/00-set-up-team-card.md | 119 ++++++++++++++++++++---------- labs/cloud/README.md | 18 +++-- labs/cloud/troubleshooting.md | 2 +- skills/data-agent-tutor/SKILL.md | 19 +++-- 9 files changed, 116 insertions(+), 69 deletions(-) diff --git a/.env.cloud.example b/.env.cloud.example index 7052acb..1826e83 100644 --- a/.env.cloud.example +++ b/.env.cloud.example @@ -4,7 +4,8 @@ # cp .env.cloud.example .env # # labs/cloud/00-set-up.md, step 2, has the snctl command for each address. -# With a team card, every value comes from the card instead: +# In an environment the organizers created for your team, the values come from +# your row in their environment sheet, and you create the API key yourself: # labs/cloud/00-set-up-team-card.md, step 2. # (The Local course writes its own .env: see labs/local/00-set-up.md.) diff --git a/README.md b/README.md index c18cdef..3691374 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The same five labs, on two stacks. | | [Cloud course](labs/cloud/README.md) | [Local course](labs/local/README.md) | |---|---|---| | Runs on | StreamNative Cloud: your own instance, with a Kafka cluster, a SQL workspace, and an agent workspace | Your laptop: [Ursa for Kafka](https://openlakestream.org/docs/ursa-for-kafka), [RisingWave](https://risingwave.com), and the Orca Agent Engine (`ork local`) | -| You need | A StreamNative Cloud login from the hackathon organizers, with a team card or an instance of your own | Docker and an Anthropic API key | +| You need | A StreamNative Cloud login from the hackathon organizers, with a team environment they created or an instance of your own | Docker and an Anthropic API key | | Time | About 40 minutes | About 45 minutes, plus image downloads | | Start | [Lab 0: Set up](labs/cloud/00-set-up.md), or [from a team card](labs/cloud/00-set-up-team-card.md) | [Lab 0: Set up](labs/local/00-set-up.md) | diff --git a/docs/before-you-arrive.md b/docs/before-you-arrive.md index 7fc92a3..95c4b29 100644 --- a/docs/before-you-arrive.md +++ b/docs/before-you-arrive.md @@ -30,8 +30,8 @@ Everyone also needs: - [`jq`](https://jqlang.org/download/), for the checks in every lab. - [`snctl`](https://docs.streamnative.io/tools/cli/snctl/snctl-overview) (the StreamNative Cloud CLI): `brew install streamnative/streamnative/snctl`. Lab 0 - uses it to read your instance's addresses and to create your topic. With a - team card (step 4) you do not need it. + uses it to read your instance's addresses and to create your topic. In an + environment the organizers created for your team (step 4) you do not need it. ## 1. Get the code @@ -74,12 +74,12 @@ Every line should say `PASS`. ## 4. Set up your instance -**Getting a team card?** If the organizers told you that your team's -environment is created for you, skip this step and create nothing in the -console: your Kafka cluster, SQL workspace, and agent workspace already exist. -Your team card has their addresses and an API key, and -[Lab 0: Set up from a team card](../labs/cloud/00-set-up-team-card.md) starts -from it. +**Did the organizers create your team's environment?** Then skip this step and +create nothing in the console: your Kafka cluster, SQL workspace, and agent +workspace already exist. The organizers share an environment sheet with a row +for your team, and +[Lab 0: Set up from a team card](../labs/cloud/00-set-up-team-card.md) turns +that row into your `.env`. The organizers add you to the hackathon organization on StreamNative Cloud, give you an **instance** of your own, and make a **service account** in it. They give diff --git a/docs/tutor.md b/docs/tutor.md index 0524593..298350e 100644 --- a/docs/tutor.md +++ b/docs/tutor.md @@ -49,7 +49,7 @@ last row works in any agent that can read a file. Started with no request, the tutor asks three things: ```text -1. Course: Cloud (on StreamNative Cloud, with a team card from the organizers or with your own instance: say which) or Local (everything on your laptop)? +1. Course: Cloud (on StreamNative Cloud, in a team environment the organizers created or in your own instance: say which) or Local (everything on your laptop)? 2. Path: CLI, Python, or TypeScript? 3. What now: start at Lab 0, resume at a lab, quiz me on a lab, or check my setup? ``` @@ -59,7 +59,7 @@ Started with no request, the tutor asks three things: | You want to | Say | |---|---| | Take a course from the start | `Start the Cloud course on the Python path.` | -| Start from a team card | `Start the Cloud course on the Python path. I have a team card.` | +| Start in a team environment | `Start the Cloud course on the Python path. My team has a row in the organizers' environment sheet.` | | Pick up where you stopped | `Resume the Local course at Lab 3. I'm on TypeScript.` | | Be quizzed | `Quiz me on Lab 2.` | | Find out why something fails | `Check my setup.` Or paste the error. | diff --git a/labs/README.md b/labs/README.md index d9fe406..25daf5a 100644 --- a/labs/README.md +++ b/labs/README.md @@ -5,7 +5,7 @@ Two courses teach the same five labs on two stacks. Pick one. | | [Cloud course](cloud/README.md) | [Local course](local/README.md) | |---|---|---| | Runs on | StreamNative Cloud: your own instance, with a Kafka cluster, a SQL workspace, and an agent workspace | Your laptop: Ursa for Kafka, RisingWave, and the Orca Agent Engine | -| You need | A StreamNative Cloud login from the hackathon organizers, with a team card or an instance of your own | Docker and an Anthropic API key | +| You need | A StreamNative Cloud login from the hackathon organizers, with a team environment they created or an instance of your own | Docker and an Anthropic API key | | Time | About 40 minutes | About 45 minutes, plus the image downloads | | Take it when | You are at the event | You have no StreamNative Cloud instance, or you want to see every part run | diff --git a/labs/cloud/00-set-up-team-card.md b/labs/cloud/00-set-up-team-card.md index 547e7fa..94e954d 100644 --- a/labs/cloud/00-set-up-team-card.md +++ b/labs/cloud/00-set-up-team-card.md @@ -1,23 +1,28 @@ # Lab 0: Set up from a team card -**Cloud course** · 5 minutes, plus 5 on your own · CLI, Python, or TypeScript +**Cloud course** · 8 minutes, plus 5 on your own · CLI, Python, or TypeScript -You put your team card in `.env`, load the login stream into your team's Kafka -cluster, and run the doctor. When this lab is done, the Agent Engine, Kafka, -and Schema Registry on your card all answer, and your topic holds 246 logins. +The organizers created an environment for your team on StreamNative Cloud. You +build your **team card** from it, put it in `.env`, load the login stream into +your team's Kafka cluster, and run the doctor. When this lab is done, the Agent +Engine, Kafka, and Schema Registry on your card all answer, and your topic +holds 246 logins. -No team card? [Lab 0: Set up](00-set-up.md) starts from an instance of your own -instead. Both end in the same place, and Lab 1 is the same after either. +Your team is not in the organizers' environment sheet? +[Lab 0: Set up](00-set-up.md) starts from an instance of your own instead. Both +end in the same place, and Lab 1 is the same after either. ## Before you start -- You have your **team card** from the organizers: an **API key**, and the - addresses of the environment they created for your team on StreamNative - Cloud. That environment is a Kafka cluster, a SQL workspace that imports it, - an agent workspace, and the service account the key belongs to. Everything on - the card already exists: you create nothing, and you do not need `snctl`. -- You can log in to StreamNative Cloud, and the organizers added your login to - your team's environment. This lab does not use that login; Labs 2 and 3 do. +- The organizers created your team's environment and shared the **environment + sheet** with you. On its **Team Environments** tab, one row is your team's: + the names and addresses of a Kafka cluster, a SQL workspace that imports it, + an agent workspace, and a service account. All of it already exists. In this + lab you create one thing, an API key, and you do not need `snctl`. +- You know your team's number: it is the **Team ID** of your row. +- You can log in to the StreamNative Cloud console, in the organization your + row names. You create your API key there in step 2, and Labs 2 and 3 use the + same login. - You cloned this repository and opened a terminal in it. The terminal runs `bash`: on Windows that is WSL or Git Bash, on every path, because the checks are `bash` commands. @@ -67,7 +72,10 @@ PASS jq found All good: you're ready. ``` -## Step 2: Fill in `.env` from your team card +## Step 2: Build your team card in `.env` + +Your team card is your team's row in the environment sheet, plus an API key +that you create. `.env` is where you write it down. Open a second terminal at the repository root and copy the template: @@ -75,30 +83,62 @@ Open a second terminal at the repository root and copy the template: cp .env.cloud.example .env ``` -`.env` is git-ignored. It will hold your team's key: do not commit it or paste -it anywhere. Give each of these lines its value from the card: +`.env` is git-ignored. It will hold your key: do not commit it or paste it +anywhere. + +**From the sheet.** Open the environment sheet on the **Team Environments** tab +and find the row with your **Team ID**. Six lines of `.env` come from that row: -| `.env` line | On your card | Write it as | +| `.env` line | Column in your row | Write it as | |---|---|---| -| `SN_API_KEY` | API key | the raw key, with no `token:` in front | -| `SN_SERVICE_ACCOUNT` | Service account | `@.auth.streamnative.cloud`. If the card has only the name, add the rest, with the organization id from the card (`o-...`) | -| `ORCA_BASE_URL` | Agent workspace endpoint | `https://` and the host, with no `/v1` | -| `KAFKA_BOOTSTRAP_SERVERS` | Broker URL | the host and its port, `:9093` | -| `SCHEMA_REGISTRY_URL` | Schema registry URL | `https://` and the host | -| `SN_MCP_URL` | SQL workspace MCP endpoint | `https://mcp.streamnative.cloud/mcp/x//sqlworkspace.compute.streamnative.io/` | -| `SN_SQL_DATABASE` | SQL database | as given. It is the name of your SQL catalog, not of your SQL workspace | - -If your card already is a list of `NAME=value` lines, paste each one over the -empty line with the same name. - -`SN_SQL_DATABASE` is the database you use in Lab 2 and the agent targets in -Labs 3 and 4. Leave the other lines as they are: the MCP server uses a separate -browser login in Lab 3, so `SN_MCP_AUTH=oauth` stays, and `SN_MCP_OAUTH_ISSUER` -stays empty. - -**Two people share one team card.** Your teammate fills in the same values, and -you both work in the same Kafka cluster and the same SQL database. Your agents -stay apart: each is named after its owner's OS user name, like +| `SN_SERVICE_ACCOUNT` | **Service Account**, with **StreamNative Cloud Organization** | `@.auth.streamnative.cloud`. A cell that already ends in `.auth.streamnative.cloud` goes in as it is | +| `ORCA_BASE_URL` | **Agent Workspace Endpoint** | `https://` and the host, with no `/v1` | +| `KAFKA_BOOTSTRAP_SERVERS` | **Broker URL** | as it is: the host and its port, `:9093` | +| `SCHEMA_REGISTRY_URL` | **Schema Registry URL** | as it is, with `https://` | +| `SN_MCP_URL` | **SQL Workspace MCP Endpoint** | as it is | +| `SN_SQL_DATABASE` | **SQL Database** | as it is. It is the database you open in Lab 2, and it is not your SQL workspace's name | + +A cell you need is empty? Ask a facilitator. One value you can build yourself: +the MCP endpoint is +`https://mcp.streamnative.cloud/mcp/x//sqlworkspace.compute.streamnative.io/`, +from two other cells of your row. + +**Your API key.** The sheet holds no keys. You create one in the StreamNative +Cloud console, for the service account in your row: + +1. Log in to the console, in the organization your row names. +2. Open the organization's **Settings**. Under **Access & Control**, click + **Service Accounts**, then click your team's service account. +3. Click **Create API key**. Give the key a **Name** that nobody else in the + organization uses, in lowercase letters, digits, and dashes: your team and + your name work, such as `team07-ana`. Leave **Expiration** at 30 days, and + click **Create**. +4. The next window shows the key, once. Click **Copy**, paste the key into + `.env` as `SN_API_KEY` with nothing in front of it, then click **Close**. If + you lose the key, create another. + +If **Create API key** is greyed out, your login may not create keys: ask a +facilitator. + +Leave the other lines as they are: the MCP server uses a separate browser login +in Lab 3, so `SN_MCP_AUTH=oauth` stays, and `SN_MCP_OAUTH_ISSUER` stays empty. + +Your finished card has these seven lines filled in: + +```text +SN_API_KEY= +SN_SERVICE_ACCOUNT=@.auth.streamnative.cloud +ORCA_BASE_URL=https:// +KAFKA_BOOTSTRAP_SERVERS=:9093 +SCHEMA_REGISTRY_URL=https:// +SN_MCP_URL=https://mcp.streamnative.cloud/mcp/x//sqlworkspace.compute.streamnative.io/ +SN_SQL_DATABASE= +``` + +**Two people share one environment.** Your teammate fills in the same six lines +from the same row, and can create a key of their own for the same service +account. You both work in the same Kafka cluster and the same SQL database. +Your agents stay apart: each is named after its owner's OS user name, like `hello-agent-ana`. If the two of you have the same user name, each set `PARTICIPANT` in `.env` to a name of your own. In Lab 2 the view and the table are created once for the team: if your teammate got there first, the `CREATE` @@ -190,7 +230,7 @@ Still failing after two tries? Raise your hand, or see - A. Fix it now: the doctor has to print only `PASS`. - B. Nothing yet: Lab 3 does the browser login this check waits for. -- C. Ask for a new team card. +- C. Create a new API key.
Answer @@ -263,8 +303,9 @@ Engine check, tells you the exact value to use, and ends with ## Recap -- `.env` holds your team card: the addresses of your team's environment and its - key. It is git-ignored. +- `.env` holds your team card: six values from your team's row in the + environment sheet, and an API key you created for your team's service + account. It is git-ignored. - Your team shares that environment. The login stream is loaded once, and the seeder refuses a second copy. - The doctor checks each service on the card and prints the fix for a failure. diff --git a/labs/cloud/README.md b/labs/cloud/README.md index fbd3129..36ead96 100644 --- a/labs/cloud/README.md +++ b/labs/cloud/README.md @@ -25,7 +25,7 @@ flowchart LR | Lab | Time | Where | You | The idea | |---|---|---|---|---| | [0. Set up](00-set-up.md) | 10 min | terminal | Fill in `.env` from your instance, load the topic, run the doctor | Check service access before you build on it | -| or [0. Set up from a team card](00-set-up-team-card.md) | 5 min | terminal | Fill in `.env` from your team card, load the topic, run the doctor | The same, when the organizers created your environment | +| or [0. Set up from a team card](00-set-up-team-card.md) | 8 min | terminal | Build your team card from the organizers' environment sheet, load the topic, run the doctor | The same, when the organizers created your environment | | [1. Hello, agent](01-hello-agent.md) | 5 min | CLI / Python / TS | Create an agent and chat | Agent, environment, session, events | | [2. Hello, streaming SQL](02-streaming-sql.md) | 8 min | SQL Workspace | Build a materialized view over the topic | Context that keeps itself fresh | | [3. Agent + live context](03-live-context.md) | 9 min | CLI / Python / TS | Give the agent SQL tools, inject new data | The answer changes with the data | @@ -44,8 +44,9 @@ your own. - One path installed, plus `ork`, `jq`, and `snctl`. All of this is in [Before you arrive](../../docs/before-you-arrive.md). -**Have a team card?** Then the organizers created all of this for your team, -and the card has its addresses and an API key. You need only your login, one +**Is your team in the organizers' environment sheet?** Then they created all +of this for your team. Your team's row has the addresses, and you create an API +key yourself: together they are your team card. You need only your login, one path, `ork`, and `jq`, and you start with [Lab 0: Set up from a team card](00-set-up-team-card.md). @@ -67,14 +68,15 @@ cluster was Serverless; the SQL workspace ran RisingWave 3.1.0-alpha. Python path. On the TypeScript path, the doctor, and the seeder against the topic once it was loaded. - **Lab 0 from a team card**: added on 6 October 2026 and run that day against - the same test instance, from a card of ready-made `NAME=value` lines. On the + the same test instance, with the seven lines of step 2 filled in. On the Python path: every step and check, and the failing doctor run its solution shows. On the TypeScript path, and with the CLI column's commands: the doctor and the seeder. The topic was loaded already (274 events, after earlier Lab 3 - runs), so the seeder printed its "already holds" line each time. The card had - no `SN_SQL_DATABASE` line; nothing in Lab 0 reads that value. Not run from - this page: the seeder on an empty topic, a card of labeled values, and an - environment the organizers created. + runs), so the seeder printed its "already holds" line each time. Nothing in + Lab 0 reads `SN_SQL_DATABASE`. Not run from this page: reading the values + from an environment sheet, creating the API key (its steps follow the + console's labels as of 3 October 2026; the key used was an existing one), the + seeder on an empty topic, and an environment the organizers created. - **Lab 2**: every statement and check, through `psql`. The console was not used. - **Labs 1, 3 and 4**: every step and check, on all three paths, with the model diff --git a/labs/cloud/troubleshooting.md b/labs/cloud/troubleshooting.md index cdd81dd..4dbfbfa 100644 --- a/labs/cloud/troubleshooting.md +++ b/labs/cloud/troubleshooting.md @@ -14,7 +14,7 @@ Still stuck after two tries? Raise your hand. | Symptom | Fix | |---|---| | `pip install -r requirements.txt`: `No matching distribution found for runorca==0.3.0` | The `python3` that made your virtual environment is older than the course needs: on macOS, Apple's own is 3.9. Install Python 3.11 or newer, then make the environment again with it. In `python/`: `rm -rf .venv`, then the install commands from Lab 0 with that Python's name in place of `python3`, for example `python3.13 -m venv .venv`. | -| Doctor: `Agent Engine HTTP 401/403` | The key was rejected. A key created before its permissions must be re-created: ask a facilitator. | +| Doctor: `Agent Engine HTTP 401/403` | The key was rejected. A key created before its permissions must be re-created. With a team card, create a new key yourself (Lab 0, step 2) and put it in `SN_API_KEY`; otherwise ask a facilitator. | | Doctor: `Kafka ... authentication` | `SN_SERVICE_ACCOUNT` must be the full principal, `@.auth.streamnative.cloud`; `SN_API_KEY` is the raw key. | | Doctor: `Kafka security.login_events: not found` | The topic is not there yet. Create it and load it: Lab 0, step 3. | | Doctor: `Schema Registry ... not found` | The schema is registered when you load the topic: Lab 0, step 3. | diff --git a/skills/data-agent-tutor/SKILL.md b/skills/data-agent-tutor/SKILL.md index 9c11361..b64cf47 100644 --- a/skills/data-agent-tutor/SKILL.md +++ b/skills/data-agent-tutor/SKILL.md @@ -43,18 +43,20 @@ pages, in `labs/cloud/` and in `labs/local/`: `03-live-context.md` · `04-act-with-approval.md` · `troubleshooting.md` **Lab 0 of the Cloud course has two pages.** Which one is theirs depends on one -thing: a **team card**, the addresses of an environment the organizers created -for their team, with an API key. +thing: whether the organizers created their team's environment and listed it in +the environment sheet they shared. That row, with an API key the learner +creates, is their **team card**. -- **They have a team card**: `labs/cloud/00-set-up-team-card.md`. -- **They have none**, and look up their own instance with `snctl`: `00-set-up.md`. -- **They have not said**: ask only this, and wait: "Did the organizers give - your team a team card?" +- **Their team has a row in the sheet**: `labs/cloud/00-set-up-team-card.md`. +- **It has none**, and they look up their own instance with `snctl`: `00-set-up.md`. +- **They have not said**: ask only this, and wait: "Did the organizers create + your team's environment, with a row for your team in their environment + sheet?" Labs 1 to 4 are the same pages after either one. - **They named no course, or no lab and no task**: reply with only this menu. - 1. Course: **Cloud** (on StreamNative Cloud, with a team card from the organizers or with your own instance: say which) or **Local** (everything on your laptop)? + 1. Course: **Cloud** (on StreamNative Cloud, in a team environment the organizers created or in your own instance: say which) or **Local** (everything on your laptop)? 2. Path: **CLI**, **Python**, or **TypeScript**? 3. What now: **start** at Lab 0, **resume** at a lab, **quiz me** on a lab, or **check my setup**? - **They pasted an error or a `FAIL` line**: give the fix the troubleshooting @@ -62,7 +64,8 @@ Labs 1 to 4 are the same pages after either one. - **They named a lab**: send its first step now. Do not ask them to confirm what they already said. Ask for their path only when the step you are about to send has one column per path and they have not named theirs. Lab 0 of the - Cloud course comes after the team card question, when they have not said. + Cloud course comes after the environment sheet question, when they have not + said. If there is no `labs/` folder in the working directory or above it, say so, ask them to open you in their clone of the repository, and stop. Never teach a lab