Central monorepo for the OpenTechEvents organizer kit: npm connectors (pure functions, no UI), reusable GitHub Actions workflows, and (phase 2) the web dashboard/editor/previewer/embeddable widget. Design rationale lives in DESIGN.md; the spec lives in opentechevents-spec.
| Package | What it does |
|---|---|
@opentechevents/validate |
Validates OTE Event/Feed documents against the version each one declares. Every published spec version is embedded. |
@opentechevents/migrate |
Moves a document to a newer spec version, and warns where D032's multi-part pricing change silently altered what it means. |
@opentechevents/export-ics |
Valid OTE Feed → iCalendar (RFC 5545). |
@opentechevents/export-rss |
Valid OTE Feed → RSS 2.0. |
@opentechevents/export-jsonld |
Valid OTE Feed/Event → schema.org Event JSON-LD (SEO structured data). |
@opentechevents/import-ics |
iCalendar (.ics) → partial OTE event documents (review-and-complete). |
@opentechevents/import-jsonld |
schema.org Event JSON-LD in an HTML page → partial OTE event documents. |
@opentechevents/build-feed |
events/*.json + ote.config.json → validated feed.json + feed.ics + feed.xml. |
All connectors are pure functions with a thin CLI on top. They never invent data: a field absent in the input stays absent in the output.
Most packages follow the OTE spec minor they implement (0.6.x speaks OTE
spec v0.6). migrate is the exception and is versioned on its own: it spans
versions rather than implementing one. Each package also has its own
changelog so fixes and improvements can be traced package by package.
These are not published to npm ("private": true). They exist because
two apps in this repo needed the same logic and a second copy would have
drifted — not as libraries for outside use, so they carry no compatibility
promise and can change shape whenever their callers do.
| Package | What it does | Used by |
|---|---|---|
@opentechevents/discover-feed |
Reference implementation of feed discovery: response bytes + content-type + URL → candidate feed URLs. No network. | apps/validator, workers/validator |
@opentechevents/feed-urls |
?repo=owner/name → the URLs a browser can actually fetch a fork's published files from, including the custom-domain cases. |
apps/publish, apps/preview, apps/editor |
@opentechevents/preview-feed |
Normalizes the three export formats (JSON, ICS, RSS) into one PreviewFeed/PreviewEvent shape for display. |
apps/embed, apps/preview, apps/editor |
| App | What it does |
|---|---|
editor |
Static web editor for OTE events: form → event JSON → prefilled issue or direct edit. |
preview |
Static feed previewer for OTE organizer forks. |
publish |
"Broadcast" console: one event → every channel it can be published to. schema.org snippet, widget and subscribe links work today; directories, newsletters and social posts are declared and unbuilt. |
embed |
Embeddable <ote-events> web component: drop an OTE feed into any website. |
validator |
Is this document a valid OTE feed or event? Three input modes (URL, file, paste), linkable results, errors pointed at the exact line. Served at validator.opentechevents.org by workers/validator, not from Pages. |
dashboard-checks |
Client-side setup checks + template-update banner for OTE organizer dashboards. |
editor, preview, publish and embed are built and deployed together by
deploy-tools.yml to tools.opentechevents.org/<tool>/;
dashboard-checks.js is served as a standalone file. Those four take a
?repo=owner/name context from an organizer's fork, which is what the shared
host expresses.
The validator does not: it serves anyone with a JSON document, so it gets
its own hostname, validator.opentechevents.org, deployed with the Worker
that its URL mode needs anyway (deploy-validator.yml). The Pages path
/validator/ remains as a redirect.
| Skill | What it does |
|---|---|
ote-event |
Agent Skill: an organizer asks in plain language ("create the OTE spec for this conference and add it to our site") and the agent drives the connectors above — discovery, import, validate, the <link> tag, the badge, JSON-LD. |
Agent Skills are an open format (a folder with a SKILL.md), so one copy works in
VS Code with Copilot, Claude Code, Codex and anything else that reads them. They add
no logic: the mapping and the verdicts stay in the packages, and the skill relays
their output — see skills/README.md for how an organizer
installs and uses one, and skills/evals/ for how the skills are
measured.
| Worker | What it does |
|---|---|
validator |
Serves validator.opentechevents.org: the validator page and its fetch endpoint on one origin. The only component with network access. |
The endpoint exists for one mode of one tool: the validator cannot fetch a third-party feed from the browser, because community feeds send no CORS headers. This is not the "CORS proxy for reading platforms" that DESIGN.md rules out — it fetches a document the user already has the URL of, in order to validate it, and stores nothing. Serving the page from the same Worker means the page never makes a cross-origin request at all.
Organizer repos (forks of ote-template) don't copy any build logic — they
call these workflows with uses:. The fork stays ~5 lines per workflow and
improvements arrive by moving the @v1 tag here.
Validates events/*.json and ote.config.json; fails with the offending
file and field. In the consuming repo:
# .github/workflows/validate.yml
name: Validate
on:
pull_request:
jobs:
validate:
uses: OpenTechEvents/ote-tools/.github/workflows/validate.yml@v1Builds the feed (feed.json, feed.ics, feed.xml), copies the repo's
docs/ on top of the site root, and deploys everything to GitHub Pages. In
the consuming repo:
# .github/workflows/pages.yml
name: Publish
on:
push:
branches: [main]
jobs:
pages:
uses: OpenTechEvents/ote-tools/.github/workflows/build-pages.yml@v1
permissions:
contents: read
pages: write
id-token: writeOne-time setup in the consuming repo: Settings → Pages → Source: GitHub
Actions. After the first run the feed is served at
https://<user>.github.io/<repo>/feed.json (plus feed.ics, feed.xml),
with docs/index.html as the site's landing page.
Both workflows accept optional inputs:
| Input | Default | Meaning |
|---|---|---|
tools-ref |
commit of the workflow itself | Git ref of ote-tools to run the tools from. The default pins tools and workflow to the same commit, so they never drift. |
docs-dir (build-pages only) |
docs |
Directory copied to the site root. |
Versioning: consume with a major tag (@v1). Non-breaking improvements move
the tag; breaking changes ship as @v2 and are opt-in.
Node 22 + pnpm. pnpm install, then:
pnpm build # compile every package
pnpm test # vitest across the workspace
pnpm lint
pnpm typecheck
English is the official language of the repo — docs, comments, commits, identifiers. See CLAUDE.md for the contributor conventions.