Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,63 @@ At this step you can create a test user for example:



## Calendar invites

edumeet can send ICS calendar invites (RFC 5545 / RFC 6047 iTIP) for meetings scheduled in existing rooms. Attendees receive the invite in Gmail/Outlook/Apple Calendar, RSVP natively from their mail client, and the response flows back into edumeet via IMAP. Recurring meetings are supported.

### Prerequisites

Each tenant that wants to send invites needs a **dedicated SMTP mailbox on their domain** (e.g. `invites@tenantA.com`). The same mailbox is used to send invites *and* (optionally) receive RSVP replies via IMAP, so it must not be a human inbox — the poller marks processed messages as seen and ignores non-iTIP mail.

For best deliverability, ensure the mailbox's domain has valid **SPF and DKIM** records. Without them, invites will land in spam.

### Fresh install

`./utils/run-me-first.sh` already generates the two required secrets (`invites.encryptionKey` for AES-256-GCM encryption of stored SMTP/IMAP passwords, and `invites.rsvpTokenSecret` for HMAC-SHA256 RSVP tokens) and writes them into `configs/mgmt/default.json`. Nothing extra to do at deploy time.

### Existing install

Run:
```
./utils/gen-invite-secrets.sh
```
This prints an `"invites": { ... }` JSON block. Paste it at the top level of `configs/mgmt/default.json` (next to the existing `"authentication"` block) and restart the management server:
```
docker compose restart edumeet-management-server
```

> **Do not rotate these secrets** after tenants have configured invite credentials. The `encryptionKey` is required to decrypt previously stored SMTP/IMAP passwords. Rotation would invalidate every stored tenant invite config.

### Optional invite settings

The `"invites"` block supports two optional tuning knobs:

- **`imapPollIntervalMs`** (default `60000`) — how often the poller checks each tenant's IMAP inbox for new RSVP replies, in milliseconds.
- **`imapRetentionDays`** (default `30`) — how long processed RSVP emails stay in the tenant mailbox before the poller deletes them. Only `\Seen` messages (those the poller successfully processed) are touched; any unprocessed mail (welcome emails, junk, messages without a valid ICS) is never deleted. Values:
- `0` — delete immediately after processing (no audit trail)
- positive integer (e.g. `30`) — keep for N days, then delete
- `-1` — fully disable cleanup; nothing is ever deleted

### Per-tenant configuration (via management UI)

1. Log in to `https://yourdomain.com/mgmt-admin/` as a tenant admin or tenant owner.
2. Go to **Tenants**, click the tenant row to open the edit dialog.
3. Expand the **Invite email (SMTP/IMAP)** accordion.
4. Fill in the organizer address (the From: line, e.g. `invites@tenantA.com`), organizer display name, SMTP block (host, port, TLS, user, password) and optionally IMAP block.
5. Apply.

Invite workers boot automatically for that tenant — no restart needed.

### IMAP is optional

If IMAP is left blank, invites still work: attendees receive the ICS and can RSVP from their calendar client. The only thing missing is per-attendee RSVP status surfaced in the edumeet admin UI. Within the same provider (Google↔Google, Outlook↔Outlook) attendees still see each other's status natively. Tenants on Gmail/M365 that can't provide basic-auth IMAP can skip it.

> **RSVPs from Thunderbird + Google Calendar are unreliable.** When Thunderbird's calendar event comes from a Google Calendar (via CalDAV / Provider for Google), accepting or declining the invite in Thunderbird writes the new response to Google via CalDAV but does **not** send an iMIP REPLY email. Because Google is not the meeting organizer in this setup, Google will not proxy an RSVP email either. The change is invisible to edumeet and the attendee stays as NEEDS-ACTION in the admin UI. For a reliable RSVP, respond on [calendar.google.com](https://calendar.google.com) directly — that path does send an iMIP REPLY.

### Landing page

Logged-in users see a calendar icon button next to the login/logout button in the landing, join, and lobby dialogs. The button is only visible when the user's tenant has invites enabled. Clicking opens a dialog listing upcoming meetings (both organized by and invited to the user) with inline Join buttons, a refresh button, and a shortcut to the full management page.

## Logs
To see logs (add -f for tailing the logs):
```
Expand Down
6 changes: 6 additions & 0 deletions configs/mgmt/default.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,11 @@
"redirect_uri": "https://edumeet.example.com/mgmt/oauth/tenant/callback"
}
}
},
"invites": {
"encryptionKey": "REPLACE_ME_invite_encryption_key",
"rsvpTokenSecret": "REPLACE_ME_invite_rsvp_token_secret",
"imapPollIntervalMs": 60000,
"imapRetentionDays": 30
}
}
41 changes: 41 additions & 0 deletions utils/gen-invite-secrets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
#
# gen-invite-secrets.sh
#
# Generates the two secrets required for the calendar-invite workers:
# - invites.encryptionKey : 64-char hex key for AES-256-GCM at-rest encryption of
# per-tenant SMTP/IMAP passwords stored in tenantInviteConfigs
# - invites.rsvpTokenSecret : HMAC-SHA256 secret used to mint per-attendee RSVP tokens
#
# Use this on existing installs that already have configs/mgmt/default.json set up.
# It prints the JSON block to paste next to the existing "authentication" block.
# It does NOT modify any files.
#
# For fresh installs, run-me-first.sh writes these values automatically.
#

RED='\033[1;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NOCOLOR='\033[0m'

ENC_KEY=`tr -dc A-Fa-f0-9 </dev/urandom | head -c 64`
RSVP_KEY=`tr -dc A-Za-z0-9 </dev/urandom | head -c 64`

echo -e "${GREEN}Generated new invite secrets.${NOCOLOR}"
echo
echo -e "${YELLOW}Paste the following block into configs/mgmt/default.json${NOCOLOR}"
echo -e "${YELLOW}at the top level, next to the existing \"authentication\" block:${NOCOLOR}"
echo
echo ' "invites": {'
echo " \"encryptionKey\": \"${ENC_KEY}\","
echo " \"rsvpTokenSecret\": \"${RSVP_KEY}\","
echo ' "imapPollIntervalMs": 60000,'
echo ' "imapRetentionDays": 30'
echo ' }'
echo
echo -e "${RED}IMPORTANT:${NOCOLOR} keep these values safe and do NOT rotate them after"
echo " tenants have configured invite email credentials — the"
echo " encryptionKey is required to decrypt previously stored"
echo " SMTP/IMAP passwords. Restart edumeet-management-server"
echo " after adding the block."
16 changes: 15 additions & 1 deletion utils/run-me-first.sh
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,21 @@ MGMT_PRIV_ESCAPED=$(echo "$MGMT_PRIV" | sed 's%\\n%\\\\n%g')
sed -i "s%^.*MGMT_PRIV=.*$%MGMT_PRIV=\"${MGMT_PRIV_ESCAPED}\"%" .env
echo "MGMT_PRIV=\"${MGMT_PRIV}\""

sed -i -e "s%secret\":.*%secret\": \"${MGMT_PRIV_ESCAPED}\",%" configs/mgmt/default.json
sed -i -e "s%secret\":.*%secret\": \"${MGMT_PRIV_ESCAPED}\",%" configs/mgmt/default.json

# Generate invite worker secrets (AES-256-GCM key + HMAC secret for RSVP tokens)
if grep -Fq 'REPLACE_ME_invite_encryption_key' configs/mgmt/default.json
then
echo 'generating new secret for invite credential encryption'
INVITES_ENC=`tr -dc A-Fa-f0-9 </dev/urandom | head -c 64`
sed -i "s%\"encryptionKey\": \"REPLACE_ME_invite_encryption_key\"%\"encryptionKey\": \"${INVITES_ENC}\"%" configs/mgmt/default.json
fi
if grep -Fq 'REPLACE_ME_invite_rsvp_token_secret' configs/mgmt/default.json
then
echo 'generating new secret for invite RSVP tokens'
INVITES_HMAC=`tr -dc A-Za-z0-9 </dev/urandom | head -c 64`
sed -i "s%\"rsvpTokenSecret\": \"REPLACE_ME_invite_rsvp_token_secret\"%\"rsvpTokenSecret\": \"${INVITES_HMAC}\"%" configs/mgmt/default.json
fi

echo "If you want to generate cert(s) with Let's Encrypt run gen-cert.sh"
echo "You can start the application with gen-cert.sh or docker compose up"
Expand Down
Loading