Live shows: a concert kind, artists, doors and encore, and tickets - #125
Merged
Conversation
A live event now carries a kind (talk, class, concert), so one NixAmp serves a school and a venue without either knowing about the other. Concerts bring their own layout presets: a stage, a setlist, a tip jar, a merch shelf and a till. The directory can be asked for one kind. An artist is an invitation role beside moderator. They go on stage and drive the show -- doors, start, encore, end -- without being handed the guest list; cancelling and archiving stay with the host. The encore is its own state, because a listener arriving during one is arriving at something and "ended" would turn them away. A ticket is a paid pass to one room, over x402 and settled by CoinPay: the same machinery as the crawler paywall, scoped to a single event and paid to the event's own address rather than to the platform. The room answers 402 with the price instead of pretending not to be there, the ticket rides in x-nixamp-ticket or in ?ticket= for an element that cannot set a header, and each event's passes are signed with a secret derived from the event id, so a ticket to Friday is not a ticket to Saturday. Hosts can comp one, because a venue with no guest list can only answer a problem with "pay again". Nothing here is named after a site. The event page's brand is read from the shell it is injected into rather than hard-coded, and a concert's JSON-LD is a MusicEvent with an Offer on it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuUkTrofSjQ15j79mRuy4f
ralyodio
marked this pull request as ready for review
September 12, 2026 11:23
ralyodio
added a commit
that referenced
this pull request
Sep 12, 2026
After rebasing on #125 the INSERT carries kind, doors and the ticket fields, so the positional fake read status out of the wrong slot and every bridged party came back 500. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuUkTrofSjQ15j79mRuy4f
ralyodio
added a commit
that referenced
this pull request
Sep 12, 2026
… a nixamp room (#127) * Sign in to bittorrented.com with nixamp, and make its watch party a nixamp room nixamp.com becomes an OAuth 2.1 authorization server, and a watch party on a site that connects through it becomes an ordinary nixamp live event with a room -- so the same party is joinable from the web app, the terminal, the desktop app, the television and an agent over MCP, without any of them learning anything about torrents. Which side is the authorization server was the first question. nixamp already keeps the accounts, the revocable tokens and the provider identities, and bittorrented.com keeps none of those, so nixamp is the AS and bittorrented is the client. src/oauth.ts stays what it was -- nixamp signing in WITH GitHub -- and src/oauth-server.ts is the other direction. It is 2.1 rather than 2.0, which is to say the things 2.0 left optional are not optional here: PKCE with S256 on every request from every client, public or confidential; redirect URIs matched exactly, with only a loopback port allowed to vary because a CLI cannot know its port before it listens; a code spent once, where a second use withdraws everything the first produced; refresh tokens that rotate, where a retired one presented again withdraws the whole family. There is no implicit grant and no password grant, and both are refused by name rather than shrugged at. RFC 8414 metadata says so at /.well-known/oauth-authorization-server. Access tokens are nixamp's own `nxa_` tokens with a client's name on them, which is what makes them revocable and what makes every existing /api/v1 route understand one with no change. An error that cannot be redirected is a page: bouncing a refusal to an unregistered redirect URI would make this an open redirector. The bridge itself (src/watch-party.ts) is deliberately thin. A bridged party is a live_events row like any other, plus one row saying which external party it is, where to watch it, and the second the host says everybody is at. That is what makes discovery, invitations, chat, layouts and /live/:slug work with no special case. Bridging is idempotent, because the client calls it every time somebody opens the party page. A watch link must be on the client's own site, read off the redirect URIs it registered rather than configured twice. Surfaces: web gets a Watch parties panel and a Connected apps list in the Account panel; `nixamp party` gets list/join/host/sync/end; `nixamp mcp` is a new stdio MCP server -- JSON-RPC on a pipe, no dependency -- offering those same five as tools to an agent. 545 tests pass, typecheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuUkTrofSjQ15j79mRuy4f * Teach the OAuth test's fake live_events row the concert columns After rebasing on #125 the INSERT carries kind, doors and the ticket fields, so the positional fake read status out of the wrong slot and every bridged party came back 500. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuUkTrofSjQ15j79mRuy4f --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
added a commit
that referenced
this pull request
Sep 12, 2026
Two fragmented-MP4 packagings met: the relay's, with a Packaging type and a per-run token on the init segment, and the transport-stream work's, which asks for fMP4 for an H.265 channel. The relay's shape stays and the H.265 rule folds into it: playlist(id, fmp4) picks fmp4 when asked, else the compression policy's packaging. One segmentType, the finer one. The transport tests learn the tokened init name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MEguYxw44VkpjFrxxGrNGx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NixAmp core learns what a live show is, so a concert site can be as thin a client as BackToSchool.help is. Nothing here is named after a site.
A kind on every event (
talk,class,concert). It decides the layout, and/api/v1/events?kind=concertis the directory a venue reads. Concert presets:concert-viewer(stage + till),concert-ticketholder(stage, setlist, tip jar, merch, chat),concert-artist(adds soundcheck and the controls). New panels:stage-video,setlist,tip-jar,merch,tickets,lineup,soundcheck.An artist role beside moderator. Artists perform without presiding: doors, start, encore, end, publish to the room; not the guest list, not moderation. Cancelling and archiving stay with the host.
Doors and an encore.
POST /api/v1/events/:id/{doors,start,encore,end,cancel,archive}.encoreis its own state because a listener arriving during one is arriving at something.Tickets: a paid pass to one room. The same x402 machinery as the crawler paywall, scoped to an event and paid to the event's own
payTorather than to the platform.GET /api/v1/events/:id/tickets— price, and whether you hold onePOST /api/v1/events/:id/tickets— X-PAYMENT proof in, ticket outPOST /api/v1/events/:id/tickets/comp— the guest list, hosts onlyx-nixamp-ticketor?ticket=(an<audio src>cannot set a header)NIXAMP_TICKET_SECRET(or the CoinPay key) and the event id, so a ticket to Friday is not a ticket to SaturdayCOINPAY_X402_KEYmeans every event is simply freeAlso: the
/live/:slugpage's brand is read from the shell it is injected into instead of being hard-coded to BackToSchool.help, and a concert's JSON-LD is aMusicEventwith anOffer.Schema changes are additive and idempotent (
ADD COLUMN IF NOT EXISTS, CHECK constraints replaced by name), matching the repo's on-demand convention.Tests
bun test test web/test: 538 pass, 1 skip, 0 fail.bun run typecheck: clean. Newtest/tickets.test.ts(10) plus concert cases intest/layouts.test.ts,test/live-events.test.tsandtest/live-api.test.ts.The client is c0ncerts.com, in a PR of its own.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QuUkTrofSjQ15j79mRuy4f