### Start Gathio Application (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Demonstrates two methods to start the Gathio application: directly in the terminal as the 'gathio' user for testing, and using systemctl to start it as a background service. ```bash # start locally in terminal as gathio user cd /srv/gathio sudo -u gathio /usr/bin/pnpm start # start service to run in background sudo systemctl start gathio ``` -------------------------------- ### Configure Gathio (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Copies the example configuration file to a new file that can be edited. This step is crucial for customizing Gathio's settings, such as email formatting, to match the local environment. ```bash cp config/config.example.toml config/config.toml $EDITOR config/config.toml ``` -------------------------------- ### Install Gathio Dependencies and Build (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Navigates into the Gathio directory, installs project dependencies using pnpm, and then builds the project. Note that build errors due to type-checking can be ignored as the output is still generated. ```bash cd gathio pnpm install pnpm build ``` -------------------------------- ### Install pnpm Package Manager (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Installs the pnpm package manager globally using a script and creates a symbolic link to make it accessible. This is a prerequisite for managing Gathio's Node.js dependencies. ```bash export PNPM_HOME="/usr/.pnpm" curl -fsSL https://get.pnpm.io/install.sh | sh - sudo ln -s /usr/.pnpm/pnpm /usr/bin/pnpm ``` -------------------------------- ### Set up Gathio Systemd Service (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Copies the systemd service file to the system's service directory and reloads the systemd daemon to recognize the new service. This prepares Gathio to run as a background service. ```bash sudo cp gathio.service /etc/systemd/system/ sudo systemctl daemon-reload ``` -------------------------------- ### Clone Gathio Repository (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Clones the Gathio repository from GitHub to the /srv/ directory on a Linux system. This is the first step in the self-hosting installation process. ```bash cd /srv/ sudo git clone https://github.com/lowercasename/gathio/ ``` -------------------------------- ### Create Gathio User and Set Permissions (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Creates a dedicated system user 'gathio' with a specific home directory and sets the ownership of the Gathio files to this new user. It also includes a check to ensure the user can access pnpm. ```bash sudo adduser --home /srv/gathio --disabled-login gathio sudo chown -R gathio:gathio /srv/gathio cd / && sudo -u gathio /usr/bin/pnpm --version ``` -------------------------------- ### Create Static Page - Markdown Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/customization.md Example of a Markdown file for a static page, such as a privacy policy. It supports standard Markdown formatting. ```markdown # static/privacy-policy.md This is an example privacy policy. It **supports Markdown!** ``` -------------------------------- ### Verify Gathio Listening Port (Bash) Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/installation.md Uses netstat to check if the Gathio application is listening on port 3000. This confirms the service is running and accessible. It also explains dual-stack IPv6/IPv4 listening behavior in Linux. ```bash $ sudo netstat -tunap | grep LISTEN [...] tcp6 0 0 :::3000 :::* LISTEN 5655/node [...] ``` -------------------------------- ### Create and Query Event Documents in MongoDB with Mongoose Source: https://context7.com/lowercasename/gathio/llms.txt Provides a comprehensive TypeScript example using Mongoose to interact with MongoDB event schemas. It covers creating a new event document with various fields including ActivityPub integration details, saving it to the database, querying upcoming events, and updating event documents to add attendees or comments. ```typescript import mongoose from "mongoose"; import Event from "./models/Event.js"; // Create new event document const event = new Event({ id: "abc123xyz789def456gh", // 21-character nanoid type: "public", name: "Tech Meetup 2026", location: "123 Main St, New York, NY", start: new Date("2026-02-15T18:00:00Z"), end: new Date("2026-02-15T20:00:00Z"), timezone: "America/New_York", description: "Monthly tech meetup", image: "abc123xyz789def456gh.jpg", url: "https://example.com", creatorEmail: "organizer@example.com", hostName: "Jane Doe", editToken: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", showOnPublicList: true, usersCanAttend: true, usersCanComment: true, maxAttendees: 50, firstLoad: true, attendees: [], comments: [], followers: [], activityPubActor: JSON.stringify({ "@context": "https://www.w3.org/ns/activitystreams", "type": "Person", // ... full actor object }), activityPubEvent: JSON.stringify({ "@context": "https://www.w3.org/ns/activitystreams", "type": "Event", // ... full event object }), publicKey: "-----BEGIN PUBLIC KEY-----\n...", privateKey: "-----BEGIN PRIVATE KEY-----\n...", activityPubMessages: [] }); await event.save(); // Query events const upcomingEvents = await Event.find({ start: { $gte: new Date() }, showOnPublicList: true }).sort({ start: 1 }).limit(10); // Add attendee await Event.updateOne( { id: "abc123xyz789def456gh" }, { $push: { attendees: { name: "John Smith", status: "attending", email: "john@example.com", removalPassword: "adamant-stairway-chisel-cement-minnow-snowman", number: 2, visibility: "public", created: new Date() } } } ); // Add comment await Event.updateOne( { id: "abc123xyz789def456gh" }, { $push: { comments: { id: "comment_" + Date.now(), author: "Alice", content: "Great event!", timestamp: new Date(), replies: [] } } } ); ``` -------------------------------- ### Email Service Integration and Usage (TypeScript) Source: https://context7.com/lowercasename/gathio/llms.txt Details the integration and usage of the Gathio email service, including initialization with configuration and an Express Handlebars instance. It provides examples for sending simple emails, emails using templates, and sending to multiple recipients with BCC. ```typescript import { EmailService } from "./lib/email.js"; import { getConfig } from "./lib/config.js"; import { create as createHandlebars } from "express-handlebars"; const config = getConfig(); const hbsInstance = createHandlebars({ defaultLayout: "main", layoutsDir: "views/layouts/", partialsDir: ["views/partials/"] }); const emailService = new EmailService(config, hbsInstance); // Verify connection (nodemailer only) await emailService.verify(); // Send simple email await emailService.sendEmail({ to: "user@example.com", subject: "Test Email", text: "This is a plain text email", html: "

This is an HTML email

" }); // Send email using template await emailService.sendEmailFromTemplate({ to: "user@example.com", subject: "Your Event Has Been Created", templateName: "createEvent", templateData: { eventID: "abc123xyz789def456gh", editToken: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" } }); // Send to multiple recipients with BCC await emailService.sendEmailFromTemplate({ to: "organizer@example.com", bcc: ["attendee1@example.com", "attendee2@example.com", "attendee3@example.com"], subject: "Event Updated", templateName: "editEvent", templateData: { diffText: "", eventID: "abc123xyz789def456gh" } }); ``` -------------------------------- ### Token-based Authentication Middleware with Express Source: https://context7.com/lowercasename/gathio/llms.txt This TypeScript snippet demonstrates setting up token-based authentication middleware for an Express.js application. It uses functions 'getConfigMiddleware' and 'checkMagicLink' from './lib/middleware.js' to inject configuration and protect routes. The example shows how to verify edit tokens manually and handle magic links for protected routes. ```typescript import { checkMagicLink, getConfigMiddleware } from "./lib/middleware.js"; import { Router } from "express"; const router = Router(); // Inject configuration into res.locals router.use(getConfigMiddleware); // Protected route requiring magic link (when email restriction enabled) router.post("/event", checkMagicLink, async (req, res) => { // If magic link required but invalid, returns 403 // If magic link not required, passes through // req.magicLinkEmail available if magic link used const eventData = req.body; // ... create event }); // Verify edit token manually router.put("/event/:eventID", async (req, res) => { const event = await Event.findOne({ id: req.params.eventID }); if (!event) { return res.status(404).json({ errors: [{ message: "Event not found" }] }); } if (event.editToken !== req.body.editToken) { return res.status(403).json({ errors: [{ message: "Invalid edit token" }] }); } // Token valid, proceed with update // ... }); ``` -------------------------------- ### Retrieve NodeInfo Metadata using cURL Source: https://context7.com/lowercasename/gathio/llms.txt Demonstrates how to retrieve instance metadata from Gathio using cURL. This command fetches NodeInfo version 2.2 metadata by sending an HTTP GET request to the `.well-known/nodeinfo/2.2` endpoint with an 'Accept' header for JSON. ```bash curl https://gath.io/.well-known/nodeinfo/2.2 \ -H "Accept: application/json" ``` -------------------------------- ### EventGroup Schema and Operations (TypeScript) Source: https://context7.com/lowercasename/gathio/llms.txt Defines the EventGroup schema for organizing events and provides examples for creating, updating (adding subscribers), and querying EventGroups. It requires the EventGroup model and interacts with a database for persistence. ```typescript import EventGroup from "./models/EventGroup.js"; // Create event group const eventGroup = new EventGroup({ id: "grp123abc456def789xyz", name: "NYC Tech Meetups", description: "Monthly technology meetups in New York City", image: "grp123abc456def789xyz.jpg", url: "https://technyc.example.com", creatorEmail: "admin@example.com", hostName: "Tech Community NYC", editToken: "g1h2i3j4k5l6m7n8o9p0q1r2s3t4u5v6", firstLoad: true, showOnPublicList: true, events: [], // References to Event._id subscribers: [] }); await eventGroup.save(); // Add subscriber await EventGroup.updateOne( { id: "grp123abc456def789xyz" }, { $push: { subscribers: { email: "subscriber@example.com" } } } ); // Find group with events const group = await EventGroup.findOne({ id: "grp123abc456def789xyz" }); const events = await Event.find({ eventGroup: group._id }).sort({ start: 1 }); ``` -------------------------------- ### Loading and Using Gathio Configuration (TypeScript) Source: https://context7.com/lowercasename/gathio/llms.txt Demonstrates how to load and access Gathio's configuration settings within application code using the `getConfig` function. It shows accessing general and database settings, preparing frontend-safe configurations, and implementing access control based on email restrictions. ```typescript import { getConfig, frontendConfig, instanceRules } from "./lib/config.js"; // Load configuration (cached after first call) const config = getConfig(); console.log(config.general.domain); // "gath.io" console.log(config.general.is_federated); // true console.log(config.database.mongodb_url); // "mongodb://localhost:27017/gathio" // Get frontend-safe config for templates app.get("/", (req, res) => { const frontend = frontendConfig(res); res.render("home", { config: frontend, rules: instanceRules() }); }); // Check if email creation is restricted if (config.general.creator_email_addresses?.length > 0) { // Require magic link const allowedEmails = config.general.creator_email_addresses; const isAllowed = allowedEmails.includes(userEmail); } // Check automatic deletion setting if (config.general.delete_after_days > 0) { // Events deleted after N days past end time console.log(`Events auto-delete after ${config.general.delete_after_days} days`); } ``` -------------------------------- ### Get Known Groups Source: https://context7.com/lowercasename/gathio/llms.txt Retrieves information about groups for which group IDs and their corresponding edit tokens are provided. Useful for fetching details of managed groups. ```APIDOC ## POST /known/groups ### Description Retrieve group information for stored group IDs with valid edit tokens. ### Method POST ### Endpoint `/known/groups` ### Parameters #### Request Body - **groupId** (string) - Required - The ID of the group. - **editToken** (string) - Required - The edit token associated with the group. (This should be a JSON object where keys are group IDs and values are edit tokens.) ### Request Example ```json { "grp123abc456def789xyz": "g1h2i3j4k5l6m7n8o9p0q1r2s3t4u5v6", "grp456def789xyz123abc": "v6u5t4s3r2q1p0o9n8m7l6k5j4i3h2g1" } ``` ### Response #### Success Response - An array of group objects, each containing details like id, name, description, image, editToken, and url. #### Response Example ```json [ { "id": "grp123abc456def789xyz", "name": "NYC Tech Meetups", "description": "Monthly technology meetups in New York City...", "image": "grp123abc456def789xyz.jpg", "editToken": "g1h2i3j4k5l6m7n8o9p0q1r2s3t4u5v6", "url": "/group/grp123abc456def789xyz" } ] ``` ``` -------------------------------- ### Handling Incoming Follow Activity Source: https://github.com/lowercasename/gathio/blob/main/FEDERATION.md Describes the server-side logic for processing an incoming 'Follow' Activity. It involves fetching the actor's details, responding with an 'Accept' Activity, and sending further 'Create' Activities for event and RSVP information. ```pseudocode ON RECEIVE FOLLOW_ACTIVITY: actor_uri = FOLLOW_ACTIVITY.actor GET actor_details FROM actor_uri WITH HEADERS {'Content-Type': 'application/activity+json'} IF actor_details FOUND: ACCEPT_ACTIVITY = CREATE_ACCEPT(FOLLOW_ACTIVITY) SEND ACCEPT_ACTIVITY TO FOLLOW_ACTIVITY.actor EVENT_OBJECT = CREATE_EVENT_OBJECT(FOLLOW_ACTIVITY.object) CREATE_ACTIVITY = CREATE_CREATE_ACTIVITY(EVENT_OBJECT) SEND CREATE_ACTIVITY TO gathio_inbox WITH TO = actor_uri QUESTION_OBJECT = CREATE_QUESTION_OBJECT(FOLLOW_ACTIVITY.object) RSVP_ACTIVITY = CREATE_CREATE_ACTIVITY(QUESTION_OBJECT) SEND RSVP_ACTIVITY TO gathio_inbox WITH TO = actor_uri ``` -------------------------------- ### Gathio Configuration File Structure (TOML) Source: https://context7.com/lowercasename/gathio/llms.txt Illustrates the structure of the TOML configuration file for Gathio, covering general settings, database connection, email service specifics (Nodemailer, SendGrid, Mailgun), and static page definitions. ```toml # config/config.toml [general] domain = "gath.io" port = "3000" email = "contact@gath.io" site_name = "Gathio" is_federated = true delete_after_days = 7 # 0 = never delete email_logo_url = "https://gath.io/logo.png" show_kofi = false show_public_event_list = true mail_service = "sendgrid" # Options: "nodemailer", "sendgrid", "mailgun", "none" creator_email_addresses = [] # Empty = anyone can create, or ["user1@example.com", "user2@example.com"] [database] mongodb_url = "mongodb://localhost:27017/gathio" [nodemailer] smtp_server = "smtp.gmail.com" smtp_port = "587" smtp_username = "your-email@gmail.com" smtp_password = "your-app-password" [sendgrid] api_key = "SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" [mailgun] domain = "mg.gath.io" api_key = "key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" api_url = "https://api.mailgun.net" [[static_pages]] title = "Privacy Policy" path = "/privacy" filename = "privacy-policy.md" [[static_pages]] title = "Terms of Service" path = "/terms" filename = "terms-of-service.md" ``` -------------------------------- ### Get Known Groups Source: https://context7.com/lowercasename/gathio/llms.txt Retrieves information about event groups for which the user has valid edit tokens. Requires a JSON payload mapping group IDs to their edit tokens. ```bash curl -X POST https://gath.io/known/groups \ -H "Content-Type: application/json" \ -d '{ "grp123abc456def789xyz": "g1h2i3j4k5l6m7n8o9p0q1r2s3t4u5v6", "grp456def789xyz123abc": "v6u5t4s3r2q1p0o9n8m7l6k5j4i3h2g1" }' ``` -------------------------------- ### DELETE /event/attendee/{eventID} and GET /event/{eventID}/unattend/{hash} - Remove Attendance Source: https://context7.com/lowercasename/gathio/llms.txt Allows attendees to remove themselves using their removal password or a one-click unattend link. ```APIDOC ## Remove Attendance ### Description Allows attendees to remove themselves using their removal password or a one-click unattend link. ### Method DELETE (for removal password) GET (for one-click unattend) ### Endpoint 1. `/event/attendee/{eventID}?p={removalPassword}` 2. `/event/{eventID}/unattend/{hash}` ### Parameters #### Path Parameters - **eventID** (string) - Required - The ID of the event. - **hash** (string) - Required (for one-click unattend) - The unique hash for unattending. #### Query Parameters - **p** (string) - Required (for removal password method) - The removal password for the attendance. #### Request Body None ### Request Example ```bash # Via removal password curl -X DELETE "https://gath.io/event/attendee/abc123xyz789def456gh?p=adamant-stairway-chisel-cement-minnow-snowman" # Via one-click unattend link curl https://gath.io/event/abc123xyz789def456gh/unattend/a1b2c3d4e5f6hash ``` ### Response #### Success Response (200) Sends unattend confirmation email (for removal password method). Redirects to event page with success message (for one-click unattend). #### Response Example (No JSON response body specified, but indicates actions taken or redirects.) ``` -------------------------------- ### ActivityPub Follow Event Process Source: https://context7.com/lowercasename/gathio/llms.txt Describes the process of following an event from a fediverse account. This involves a user initiating a follow action on a platform like Mastodon, which then sends a 'Follow' activity to the Gathio inbox. Gathio responds with an 'Accept' activity, and the follower receives event details and an RSVP poll. ```bash # From Mastodon/Friendica, search for: @abc123xyz789def456gh@gath.io # Click follow button # Server sends Follow activity to Gathio inbox # Gathio responds with Accept activity # Follower receives event details and RSVP poll (if enabled) ``` -------------------------------- ### POST /event - Create Event Source: https://context7.com/lowercasename/gathio/llms.txt Creates a new event with optional image upload and returns event credentials. ```APIDOC ## POST /event ### Description Creates a new event with optional image upload and returns event credentials. ### Method POST ### Endpoint /event ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **eventName** (string) - Required - The name of the event. - **eventLocation** (string) - Required - The location of the event. - **eventStart** (string) - Required - The start date and time of the event (ISO 8601 format). - **eventEnd** (string) - Required - The end date and time of the event (ISO 8601 format). - **timezone** (string) - Required - The timezone of the event (e.g., 'America/New_York'). - **eventDescription** (string) - Optional - A description of the event, supports Markdown. - **creatorEmail** (string) - Required - The email address of the event creator. - **hostName** (string) - Optional - The name of the event host. - **eventURL** (string) - Optional - A URL for the event. - **publicBoolean** (boolean) - Optional - Whether the event is public. - **joinBoolean** (boolean) - Optional - Whether attendees can join the event. - **interactionBoolean** (boolean) - Optional - Whether interactions (comments, etc.) are enabled. - **maxAttendeesBoolean** (boolean) - Optional - Whether there is a maximum number of attendees. - **maxAttendees** (integer) - Optional - The maximum number of attendees. - **imageUpload** (file) - Optional - An image file for the event banner. ### Request Example ```bash curl -X POST https://gath.io/event \ -F "eventName=Tech Meetup 2026" \ -F "eventLocation=123 Main St, New York, NY" \ -F "eventStart=2026-02-15T18:00" \ -F "eventEnd=2026-02-15T20:00" \ -F "timezone=America/New_York" \ -F "eventDescription=Monthly tech meetup for discussing web development" \ -F "creatorEmail=organizer@example.com" \ -F "hostName=Jane Doe" \ -F "eventURL=https://example.com" \ -F "publicBoolean=true" \ -F "joinBoolean=true" \ -F "interactionBoolean=true" \ -F "maxAttendeesBoolean=true" \ -F "maxAttendees=50" \ -F "imageUpload=@event-banner.jpg" ``` ### Response #### Success Response (200) - **eventID** (string) - The unique identifier for the created event. - **editToken** (string) - A token for editing the event. - **url** (string) - The URL to access the event. #### Response Example ```json { "eventID": "abc123xyz789def456gh", "editToken": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", "url": "/abc123xyz789def456gh?e=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" } ``` ``` -------------------------------- ### Export Events to iCalendar (TypeScript) Source: https://context7.com/lowercasename/gathio/llms.txt Exports event data into the iCalendar (.ics) format. It supports exporting single events or multiple events as a calendar feed. The `exportIcal` function is imported from './helpers.js'. Example demonstrates serving the export as a file download. ```typescript import { exportIcal } from "./helpers.js"; import Event from "./models/Event.js"; // Export single event const event = await Event.findOne({ id: "abc123xyz789def456gh" }); const icsContent = exportIcal(event); // Export multiple events as calendar feed const events = await Event.find({ eventGroup: groupObjectId, start: { $gte: new Date() } }).sort({ start: 1 }); const groupFeed = exportIcal(events, "NYC Tech Meetups"); // Serve as download res.setHeader("Content-Type", "text/calendar"); res.setHeader("Content-Disposition", 'attachment; filename="event.ics"'); res.send(icsContent); ``` -------------------------------- ### Configure Static Page - TOML Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/customization.md Configuration block for TOML files to define static pages. It maps a title and path to a specific Markdown filename located in the static directory. ```toml [[static_pages]] title = "Privacy Policy" path = "/privacy" filename = "privacy-policy.md" ``` -------------------------------- ### Default Instance Description - Markdown Source: https://github.com/lowercasename/gathio/blob/main/docs/running-gathio/customization.md The default Markdown content for the instance description. This text appears at the top of public event lists and the 'About' page. It supports Markdown formatting and the special template string {{ siteName }}. ```markdown # static/instance-description.md **{{ siteName }}** is running on Gathio — a simple, federated, privacy-first event hosting platform. ``` -------------------------------- ### Handling Incoming Unfollow Activity Source: https://github.com/lowercasename/gathio/blob/main/FEDERATION.md Details the process for handling an 'Undo/Follow' Activity. The server checks for the follower in the database and removes them if found. Currently, no 'Accept/Undo' response is sent. ```pseudocode ON RECEIVE UNDO_FOLLOW_ACTIVITY: follower_uri = UNDO_FOLLOW_ACTIVITY.object.actor IF follower_exists(follower_uri): DELETE follower FROM database // No 'Accept/Undo' response is sent currently. ``` -------------------------------- ### ActivityPub WebFinger Discovery Source: https://context7.com/lowercasename/gathio/llms.txt Discovers an event's ActivityPub actor using the WebFinger protocol. Requires the resource identifier (e.g., acct:eventID@domain). ```bash curl "https://gath.io/.well-known/webfinger?resource=acct:abc123xyz789def456gh@gath.io" \ -H "Accept: application/json" ``` -------------------------------- ### POST /attendee/provision and POST /attendevent/{eventID} - RSVP to Event Source: https://context7.com/lowercasename/gathio/llms.txt Two-step process: provision an attendance slot, then confirm attendance. ```APIDOC ## RSVP to Event ### Description Two-step process: provision an attendance slot, then confirm attendance. ### Method POST ### Endpoint 1. `/attendee/provision?eventID={eventID}` 2. `/attendevent/{eventID}` ### Parameters #### Path Parameters - **eventID** (string) - Required - The ID of the event to RSVP to. #### Query Parameters None #### Request Body **Step 1: Provision attendance slot** (No request body) **Step 2: Confirm attendance** - **removalPassword** (string) - Required - The password to remove attendance. - **attendeeName** (string) - Required - The name of the attendee. - **attendeeEmail** (string) - Required - The email address of the attendee. - **attendeeNumber** (integer) - Optional - The number of attendees. - **attendeeVisible** (boolean) - Optional - Whether the attendee is visible. ### Request Example ```bash # Step 1: Provision attendance slot curl -X POST https://gath.io/attendee/provision?eventID=abc123xyz789def456gh # Response: { "removalPassword": "adamant-stairway-chisel-cement-minnow-snowman", "freeSpots": 42 } # Step 2: Confirm attendance curl -X POST https://gath.io/attendevent/abc123xyz789def456gh \ -H "Content-Type: application/json" \ -d '{ "removalPassword": "adamant-stairway-chisel-cement-minnow-snowman", "attendeeName": "John Smith", "attendeeEmail": "john@example.com", "attendeeNumber": 2, "attendeeVisible": true }' ``` ### Response #### Success Response (200) Sends confirmation email with unattend link. #### Response Example (No JSON response body specified, but indicates actions taken.) ``` -------------------------------- ### Creating ActivityPub Actors and Events Source: https://context7.com/lowercasename/gathio/llms.txt This section details how to create ActivityPub representations for events and actors using the provided TypeScript functions. ```APIDOC ## Creating ActivityPub Actors and Events ### Description This endpoint (or set of functions) allows for the creation of ActivityPub compatible actor and event objects. ### Method N/A (Client-side functions) ### Endpoint N/A (Client-side functions) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { createActivityPubActor, createActivityPubEvent } from "./activitypub.js"; // Create ActivityPub Actor (Person type) const actor = createActivityPubActor( "abc123xyz789def456gh", // eventID "gath.io", // domain publicKey, // RSA public key "

Monthly tech meetup

", // HTML description "Tech Meetup 2026", // name "123 Main St, New York, NY", // location "abc123xyz789def456gh.jpg", // image filename moment("2026-02-15T18:00:00Z"), // start time moment("2026-02-15T20:00:00Z"), // end time "America/New_York" // timezone ); // Create ActivityPub Event object const apEvent = createActivityPubEvent( "Tech Meetup 2026", // name moment("2026-02-15T18:00:00Z"), // start moment("2026-02-15T20:00:00Z"), // end "America/New_York", // timezone "Monthly tech meetup", // description "123 Main St, New York, NY" // location ); // Store in database event.activityPubActor = actor; event.activityPubEvent = apEvent; await event.save(); ``` ### Response #### Success Response (200) Returns JSON strings representing the Actor and Event objects. #### Response Example ```json { "actor": "{\"@context\": \"https://www.w3.org/ns/activitystreams\", \"id\": \"https://gath.io/actor/abc123xyz789def456gh\", \"type\": \"Person\", \"name\": \"Tech Meetup 2026\", \"summary\": \"

Monthly tech meetup

\", \"preferredUsername\": \"techmeetup2026\", \"inbox\": \"https://gath.io/actor/abc123xyz789def456gh/inbox\", \"publicKey\": { \"id\": \"https://gath.io/actor/abc123xyz789def456gh#main-key\", \"owner\": \"https://gath.io/actor/abc123xyz789def456gh\", \"publicKeyPem\": \"-----BEGIN PUBLIC KEY...\" } }" "event": "{\"@context\": \"https://www.w3.org/ns/activitystreams\", \"name\": \"Tech Meetup 2026\", \"type\": \"Event\", \"startTime\": \"2026-02-15T18:00:00Z\", \"endTime\": \"2026-02-15T20:00:00Z\", \"timezone\": \"America/New_York\", \"description\": \"Monthly tech meetup\", \"location\": \"123 Main St, New York, NY\" }" } ``` ``` -------------------------------- ### ActivityPub RSVP via Poll Process Source: https://context7.com/lowercasename/gathio/llms.txt Explains how users can RSVP to an event by responding to an ActivityPub poll. Mastodon displays RSVP options, and a user's selection ('Attending', 'Maybe', 'Not Attending') is sent as a 'Create/Note' activity to Gathio. Gathio validates the response, adds the attendee, and sends a confirmation direct message with an unattend link. ```bash # Mastodon displays RSVP poll with options: "Attending", "Maybe", "Not Attending" # User selects "Attending" - Mastodon sends Create/Note to Gathio # Gathio validates poll response and adds attendee # Gathio sends confirmation DM with unattend link ``` -------------------------------- ### ActivityPub WebFinger Discovery Source: https://context7.com/lowercasename/gathio/llms.txt Provides ActivityPub actor discovery for an event using the WebFinger protocol. This helps other ActivityPub implementations find the actor for a given resource. ```APIDOC ## GET /.well-known/webfinger ### Description Discover an event's ActivityPub actor. ### Method GET ### Endpoint `/.well-known/webfinger` ### Parameters #### Query Parameters - **resource** (string) - Required - The resource to query, typically in the format `acct:identifier@domain.com`. ### Request Example ```bash curl "https://gath.io/.well-known/webfinger?resource=acct:abc123xyz789def456gh@gath.io" \ -H "Accept: application/json" ``` ### Response #### Success Response - **subject** (string) - The JRD subject, usually the same as the `resource` query parameter. - **links** (array) - An array of links related to the resource, including the ActivityPub actor URL. #### Response Example ```json { "subject": "acct:abc123xyz789def456gh@gath.io", "links": [ { "rel": "self", "type": "application/activity+json", "href": "https://gath.io/abc123xyz789def456gh" } ] } ``` ``` -------------------------------- ### Create ActivityPub Actors and Events in TypeScript Source: https://context7.com/lowercasename/gathio/llms.txt This snippet demonstrates how to create ActivityPub representations for actors and events using TypeScript. It utilizes functions from './activitypub.js' to generate JSON objects for actors and events, which are then stored in an event object. Dependencies include the 'moment' library for date/time handling. ```typescript import { createActivityPubActor, createActivityPubEvent, broadcastCreateMessage, broadcastUpdateMessage, sendDirectMessage } from "./activitypub.js"; // Create ActivityPub Actor (Person type) const actor = createActivityPubActor( "abc123xyz789def456gh", // eventID "gath.io", // domain publicKey, "

Monthly tech meetup

", // HTML description "Tech Meetup 2026", // name "123 Main St, New York, NY", // location "abc123xyz789def456gh.jpg", // image filename moment("2026-02-15T18:00:00Z"), // start time moment("2026-02-15T20:00:00Z"), // end time "America/New_York" // timezone ); // Returns JSON string of Actor object // Create ActivityPub Event object const apEvent = createActivityPubEvent( "Tech Meetup 2026", // name moment("2026-02-15T18:00:00Z"), // start moment("2026-02-15T20:00:00Z"), // end "America/New_York", // timezone "Monthly tech meetup", // description "123 Main St, New York, NY" // location ); // Returns JSON string of Event object // Store in database event.activityPubActor = actor; event.activityPubEvent = apEvent; await event.save(); ``` -------------------------------- ### POST /import/event - Import Event from ICS Source: https://context7.com/lowercasename/gathio/llms.txt Imports an event from an iCalendar file. ```APIDOC ## POST /import/event ### Description Imports an event from an iCalendar file. ### Method POST ### Endpoint /import/event ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **icsImportControl** (file) - Required - The iCalendar file to import. - **creatorEmail** (string) - Required - The email address of the event creator. ### Request Example ```bash curl -X POST https://gath.io/import/event \ -F "icsImportControl=@meeting.ics" \ -F "creatorEmail=organizer@example.com" ``` ### Response #### Success Response (200) - **eventID** (string) - The unique identifier for the imported event. - **editToken** (string) - A token for editing the event. - **url** (string) - The URL to access the event. #### Response Example ```json { "eventID": "xyz789abc123def456gh", "editToken": "p6o5n4m3l2k1j0i9h8g7f6e5d4c3b2a1", "url": "/xyz789abc123def456gh?e=p6o5n4m3l2k1j0i9h8g7f6e5d4c3b2a1" } ``` ``` -------------------------------- ### POST /event - Create Event with Magic Link Authentication Source: https://context7.com/lowercasename/gathio/llms.txt Allows creating an event, protected by a magic link if email restrictions are enabled. ```APIDOC ## POST /event - Create Event with Magic Link Authentication ### Description This endpoint allows for the creation of new events. It utilizes middleware to enforce token-based authentication, specifically supporting magic links when email restrictions are enabled. ### Method POST ### Endpoint `/event` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **eventData** (object) - Required - The data for the event to be created. - **name** (string) - Required - The name of the event. - **startTime** (string, ISO 8601) - Required - The start time of the event. - **endTime** (string, ISO 8601) - Required - The end time of the event. - **timezone** (string) - Required - The timezone of the event. - **description** (string) - Optional - A description of the event. - **location** (string) - Optional - The location of the event. - **editToken** (string) - Required - The edit token for the event. ### Request Example ```json { "name": "Tech Meetup 2026", "startTime": "2026-02-15T18:00:00Z", "endTime": "2026-02-15T20:00:00Z", "timezone": "America/New_York", "description": "

Monthly tech meetup

", "location": "123 Main St, New York, NY", "editToken": "your_secure_edit_token" } ``` ### Response #### Success Response (200) - **event** (object) - The created event object. - **message** (string) - A success message. #### Response Example ```json { "event": { "id": "event123xyz", "name": "Tech Meetup 2026", "startTime": "2026-02-15T18:00:00Z", "endTime": "2026-02-15T20:00:00Z", "timezone": "America/New_York", "description": "

Monthly tech meetup

", "location": "123 Main St, New York, NY", "editToken": "your_secure_edit_token" }, "message": "Event created successfully." } ``` #### Error Response (403) - **errors** (array) - An array of error objects. - **message** (string) - "Invalid magic link" or similar. #### Error Response Example (403) ```json { "errors": [ { "message": "Invalid magic link" } ] } ``` ``` -------------------------------- ### Broadcasting ActivityPub Messages Source: https://context7.com/lowercasename/gathio/llms.txt Handles broadcasting create and update messages to followers, and sending direct messages. ```APIDOC ## Broadcasting ActivityPub Messages ### Description Functions to broadcast ActivityPub messages to followers, including creating new notes and updating actor profiles, as well as sending direct messages. ### Method N/A (Client-side functions) ### Endpoint N/A (Client-side functions) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript // Broadcast Create/Note (appears in timeline) const noteObject = { "@context": "https://www.w3.org/ns/activitystreams", "id": `https://gath.io/${eventID}/m/${guidObject}`, "type": "Note", "cc": "https://www.w3.org/ns/activitystreams#Public", "content": `

Event details have been updated! See here: https://gath.io/${eventID}

` }; broadcastCreateMessage(noteObject, event.followers, eventID); // Broadcast Update/Actor (profile sync) const actorObject = JSON.parse(event.activityPubActor); broadcastUpdateMessage(actorObject, event.followers, eventID); // Send direct message to specific user const dmObject = { "@context": "https://www.w3.org/ns/activitystreams", "type": "Note", "content": `@${attendee.name} Thank you for RSVPing!`, "tag": [ { "type": "Mention", "href": attendee.id, "name": attendee.name } ] }; sendDirectMessage(dmObject, attendee.id, eventID); ``` ### Response #### Success Response (200) Messages are broadcasted to the specified recipients. No direct response is returned by these functions. #### Response Example N/A ``` -------------------------------- ### Request Magic Link for Event Creation Source: https://context7.com/lowercasename/gathio/llms.txt Requests a magic link for creating events, used when creator email restrictions are enabled. Requires the authorized email address. The link is valid for 24 hours. ```bash curl -X POST https://gath.io/magic-link/event/create \ -H "Content-Type: application/json" \ -d '{ "email": "authorized@example.com" }' ``` -------------------------------- ### Broadcast ActivityPub Messages in TypeScript Source: https://context7.com/lowercasename/gathio/llms.txt This TypeScript snippet shows how to broadcast ActivityPub messages, including 'Note' types for timelines and 'Actor' updates for profile synchronization. It also includes sending direct messages to specific users. Functions like broadcastCreateMessage, broadcastUpdateMessage, and sendDirectMessage from './activitypub.js' are used. ```typescript // Broadcast Create/Note (appears in timeline) const noteObject = { "@context": "https://www.w3.org/ns/activitystreams", "id": `https://gath.io/${eventID}/m/${guidObject}`, "type": "Note", "cc": "https://www.w3.org/ns/activitystreams#Public", "content": `

Event details have been updated! See here: https://gath.io/${eventID}

` }; broadcastCreateMessage(noteObject, event.followers, eventID); // Broadcast Update/Actor (profile sync) const actorObject = JSON.parse(event.activityPubActor); broadcastUpdateMessage(actorObject, event.followers, eventID); // Send direct message to specific user const dmObject = { "@context": "https://www.w3.org/ns/activitystreams", "type": "Note", "content": `@${attendee.name} Thank you for RSVPing!`, "tag": [ { "type": "Mention", "href": attendee.id, "name": attendee.name } ] }; sendDirectMessage(dmObject, attendee.id, eventID); ```