Small integration functions for ToughTongue AI — mock user details, weather + headlines lookups, webhook capture, and a single tabbed admin UI (Data / Debug / Activity / Info).
Pair with uAgents for heavier per-customer agent APIs behind header secrets.
| Val | Role |
|---|---|
| uFns | Lightweight integration fns (/get-user-details, /webhook, …) |
| uAgents | Customer-scoped agent fns at /fns/<customer>/<fn> |
Deployed to the ttai org on Val Town (vt create … --org-name ttai).
Two independent surfaces:
- Fn calls — one global API key, sent as
Authorization: Bearer <key>(orX-API-Key: <key>) on every request. The key is auto-minted on first use, viewable and rotatable on the Data tab. There is no per-customer scoping (that's what uAgents is for) — one key, checked in constant time, guards every fn. - Admin UI (
/) — Val Town sign-in, gated to an allow-list, same pattern as uAgents. Set theADMIN_USERSenv var (Val Town sidebar) to a comma-separated list of Val Town usernames (case-insensitive). Sign-in usesstd/oauth; seebackend/auth.ts.
A single page at / with five hash-routed tabs (/#data, /#debug, /#integration, /#activity,
/#info):
| Tab | What |
|---|---|
| Data | Mock user fields (returned by /get-user-details) + the global API key |
| Debug | Pick a fn, edit its JSON body, and fire a live call from the browser — see the resolved URL, Authorization header, and an equivalent curl update live |
| Integration | Pick a fn and get its endpoint URL, auth header, description, and parameters JSON Schema — ready to paste into a ToughTongue AI tool/function definition |
| Activity | Fn catalog + a live request/webhook log |
| Info | Role, auth model, quick start, pairing with uAgents |
All POST-only, all require the API key:
| Path | Purpose |
|---|---|
/get-user-details | Returns the mock user from SQLite |
/get-information | Weather + headlines, padded to ~3s |
/get-information-slow | Weather + headlines, padded to ~20s |
/webhook | Captures a POST body for the Activity log — a regular fn like the rest, not a catch-all |
/get-information* accept an optional JSON body {"location": "Tokyo"}; without one they fall back
to the mock user's location (Data tab, default Paris).
Ready-to-run curl examples with the real deployed base URL filled in live on the Activity tab
(empty-state) and the Info tab (quick start) — no guessing your val's hostname.
Both fns call free, keyless public APIs — no signup, no rate-limit surprises for a dev tool:
- Weather — Open-Meteo (chosen). Free forecast + geocoding APIs, no API key, no rate limit for reasonable use. Alternatives considered: wttr.in (free, keyless, but plain-text/ANSI-first — JSON is an afterthought) and OpenWeatherMap (richer data, but requires a free-tier API key — adds a required env var for a mock endpoint).
- Headlines — Hacker News (Firebase) API (chosen). Free,
keyless, and consistently fast (
topstories.json+ a parallelitem/<id>.jsonfetch per story — measured ~0.3s per call). Headlines skew tech/HN, not general news. Alternatives considered: GDELT DOC 2.0 (free, keyless, broader real-world news coverage via full-text search — but measured ~13s per query from this repo's dev network, too slow/unpredictable for a 6–20s budget); NewsAPI.org / GNews / currentsapi (broader coverage and categories, but all require a free-tier API key).
backend/
api.ts HttpError, readJson/readText, dispatch (named api.ts, not
http.ts — Val Town infers a val's type from its filename;
only index.http.ts should be an HTTP val)
auth.ts OAuth admin gate + global API key check
config.ts mock-user fields, FN_SPECS (fn catalog + Debug samples)
db.ts SQLite: settings, request log, webhook log
settings-store.ts mock-user settings + the global API key
information.ts weather + headlines fetch/pad logic
fns/ one file per fn handler
pages/ view-model assembly for SSR (admin.ts, activity.ts)
frontend/
layout.tsx Shell, Tab, Panel, Picker, ReadOnlyField, UserBadge
admin.tsx composes Data/Debug/Activity/Info into the tabbed page
data.tsx, debug.tsx, activity.tsx, info.tsx one tab each
auth.tsx signed-out / access-denied pages
styles/ modular CSS layers (Next.js-style)
tokens.ts design tokens + semantic M3 aliases (palette is runtime-generated)
base.ts document layout + typography scale
components.ts cards, tabs, key row, fields/buttons/tables, debug box
index.ts composes layers → GLOBAL_CSS
From this directory:
vt create uFns . --org-name ttai --upload-if-exists --no-editor-files vt push # ship changes
val.yml is not read by vt yet — it documents the val contract for a future repo wrapper over
vt-cli.
- Write a thin handler in
backend/fns/<name>.ts— throwHttpErrorfor client errors; body parsing viareadJson/readTextfrombackend/api.ts. - Register it in
ROUTESinsideindex.http.ts, wrapped inwithAuth(...)so it requires the global API key (dispatch wraps error handling). - Add it to
FN_SPECSinbackend/config.ts(path, note, sample body) so it shows up in the Activity table and the Debug tab's picker. vt push.