### 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: "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); ```