[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80141":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":13,"stars30d":13,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":13,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":16,"fork":16,"defaultBranch":17,"hasWiki":18,"hasPages":16,"topics":19,"createdAt":10,"pushedAt":10,"updatedAt":20,"readmeContent":21,"aiSummary":22,"trendingCount":13,"starSnapshotCount":13,"syncStatus":23,"lastSyncTime":24,"discoverSource":25},80141,"purelymailcalendar","bhartiyashesh\u002Fpurelymailcalendar","bhartiyashesh","Purelymail is a great email host but its calendar story is bare-bones - events can be stored over CalDAV, but there's no way to send proper iTIP meeting invites or track RSVPs through it. This app fills that gap.","https:\u002F\u002Fpurelymailcalendar.com\u002F",null,"Python",47,0,52,1,false,"main",true,[],"2026-06-12 02:03:58","# Purelymail Calendar\n\n> Unofficial calendar and meeting-invite web app for Purelymail mailboxes — month\u002Fweek\u002Fday views, real iTIP scheduling, and automatic RSVP tracking. Live at **[purelymailcalendar.com](https:\u002F\u002Fpurelymailcalendar.com)**.\n\n![FastAPI](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fbackend-FastAPI-009688) ![React](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ffrontend-React%20%2B%20Vite-61dafb) ![Postgres](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdb-Postgres-336791)\n\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F624a75c4-c0c9-455a-b87d-88cb392d0a5e\n\n\n\n\nPurelymail is a great email host, but its calendar story is bare-bones — you can store events over CalDAV, but it doesn't ship an iMIP\u002FiTIP scheduler, so sending real meeting invites and tracking RSVPs is missing. **Purelymail Calendar** fills that gap.\n\nSign in with a magic link, connect your Purelymail mailbox once, and you get:\n\n- Month, week, and day views with click-to-create, drag-to-move, and a live \"now\" line\n- Real iTIP `REQUEST` \u002F `CANCEL` invites delivered from your own address, with the MIME structure that Apple Mail, Gmail, and Outlook all render correctly\n- An IMAP poller that pulls attendee replies, matches them to events, and updates each `PARTSTAT` automatically\n- Magic-link authentication, encrypted-at-rest credential storage (Fernet), and per-user data isolation — your calendar lives in your Purelymail mailbox, not in this app's database\n\n**Made by [Yashesh Bharti](https:\u002F\u002Fyasheshbharti.com). Not affiliated with, endorsed by, or sponsored by Purelymail.**\n\n## Architecture\n\n```\n                 SMTP (REQUEST\u002FCANCEL)            IMAP (REPLY)\n                 ────────────────►                ◄────────────\n   Purelymail Calendar ──────────► attendees ──────► Purelymail Calendar\n       │                                                            │\n       └─────► CalDAV PUT (canonical event store) ◄─────────────────┘\n               https:\u002F\u002Fpurelymail.com\u002Fwebdav\u002F\u003Caccount>\u002Fcaldav\u002F\n```\n\nServers used (per signed-in user, discovered automatically from the mailbox):\n\n| | host | port |\n|---|---|---|\n| CalDAV | `purelymail.com` | 443 (HTTPS) |\n| SMTP | `smtp.purelymail.com` | 465 (SSL) or 587 (STARTTLS) |\n| IMAP | `imap.purelymail.com` | 993 (SSL) |\n| ManageSieve | `mailserver.purelymail.com` | 4190 (STARTTLS) |\n\n## Project layout\n\n```\ncalinvite\u002F                 (upstream CLI package — see below)\ncalinvite_web\u002F             FastAPI web app (multi-user, magic-link auth)\n  app.py                   HTTP routes\n  schemas.py               Pydantic request\u002Fresponse models\n  services.py              CalDAV \u002F SMTP \u002F IMAP plumbing\n  db.py models.py          SQLAlchemy: User \u002F Mailbox \u002F Session \u002F MagicToken\n  auth.py                  Magic-link auth + session cookie + current_user dep\n  mailbox.py               POST \u002Fapi\u002Fme\u002Fmailbox (auto-discovers account ID)\n  crypto.py                Fernet helpers\n  mailer_transactional.py  Magic-link sender (Resend → SMTP fallback)\nweb\u002F                       React + Vite + TypeScript + Tailwind frontend\nsieve\u002Fcalinvite.sieve      Sieve script that routes METHOD:REPLY to an RSVPs mailbox\n```\n\n## Running locally\n\n```bash\npython3 -m venv .venv && source .venv\u002Fbin\u002Factivate\npip install -e \".[web]\"\ncp .env.example .env\n# fill in RESEND_API_KEY (or MAGIC_LINK_SMTP_* fallback) and any other vars\n.\u002Fscripts\u002Fdev.sh\n```\n\nThat brings up:\n- FastAPI on `http:\u002F\u002F127.0.0.1:8000`\n- Vite on `http:\u002F\u002F127.0.0.1:5173` (proxies `\u002Fapi\u002F*` to uvicorn)\n\nOpen `http:\u002F\u002F127.0.0.1:5173`, sign in with magic link, paste your Purelymail mailbox password when prompted. The app auto-discovers your Purelymail account ID via PROPFIND on `\u002Fwebdav\u002F`.\n\n## Environment variables\n\n```env\n# Database (SQLite by default for local; Postgres on Railway via DATABASE_URL)\nDATABASE_URL=\n\n# Magic-link delivery (preferred: Resend)\nRESEND_API_KEY=re_...\nRESEND_FROM_EMAIL=signin@yourdomain.com\nRESEND_FROM_NAME=Purelymail Calendar\n\n# SMTP fallback (used if RESEND_API_KEY is unset)\nMAGIC_LINK_SMTP_HOST=smtp.purelymail.com\nMAGIC_LINK_SMTP_PORT=465\nMAGIC_LINK_SMTP_USER=you@yourdomain.com\nMAGIC_LINK_SMTP_PASS=...\n\n# Used to build the magic-link href\nBASE_URL=https:\u002F\u002Fpurelymailcalendar.com\n\n# Encryption key for mailbox passwords at rest\nFERNET_KEY=\n\n# True in production (HTTPS)\nCOOKIE_SECURE=true\n```\n\n## Underlying CLI\n\nThe web app sits on top of an internal Python package (`calinvite\u002F`) that provides the iTIP plumbing. The CLI is still callable directly if you want to script things or run an external RSVP cron:\n\n```bash\ncalinvite create \\\n  --account YASHESH \\\n  --calendar Personal \\\n  --summary \"Quick sync\" \\\n  --start 2026-05-15T14:00 --duration 60 \\\n  --tz America\u002FChicago \\\n  --attendee \"advisor@example.com:Jane Doe\"\n\ncalinvite rsvps --account YASHESH --mailbox RSVPs\n```\n\nThe CLI uses `.env`-based single-user config (see `.env.example`); the web app uses the per-user database instead. They share the same iTIP and CalDAV layer.\n\n### Sieve (optional but recommended for the RSVP poller)\n\nBy default the RSVP poller scans INBOX. With the bundled Sieve script installed, METHOD:REPLY messages get pre-filed into a dedicated `RSVPs` folder server-side, so the poller scans a small focused mailbox and your INBOX stays clean.\n\n```bash\ncalinvite sieve install --account YASHESH   # uploads + activates the bundled script\ncalinvite rsvps --account YASHESH --mailbox RSVPs\n```\n\nThe bundled script (`sieve\u002Fcalinvite.sieve`):\n\n```sieve\nrequire [\"fileinto\", \"mailbox\", \"body\"];\n\nif body :contains \"METHOD:REPLY\" {\n    fileinto :create \"RSVPs\";\n}\n```\n\n`body :contains` is the right test because `method=REPLY` lives inside the calendar MIME part's Content-Type — a header test would miss it. `:create` makes Purelymail auto-create the `RSVPs` mailbox the first time a reply arrives.\n\n## Deploying\n\nHosted on Railway. The `Dockerfile` does a multi-stage build (Node builds the frontend, Python serves both the API and the static bundle). `railway.json` declares the build + healthcheck.\n\nPushes to `main` are built and deployed automatically by [`.github\u002Fworkflows\u002Fdeploy.yml`](.github\u002Fworkflows\u002Fdeploy.yml): the workflow builds the image with the commit SHA baked in via `--build-arg`, pushes three tags (`latest`, short SHA, full SHA) to `ghcr.io\u002Fbhartiyashesh\u002Fpurelymailcalendar`, and runs `railway up`. Manual fallback:\n\n```bash\nrailway up --service web\n```\n\nHealth: `GET \u002Fapi\u002Fhealth` returns `200 {\"ok\":true}`.\n\n## Verifying what's running\n\nThe point of this section is to make \"trust me bro\" unnecessary.\n\n1. Open \u003Chttps:\u002F\u002Fpurelymailcalendar.com\u002Fapi\u002Fversion>. The response includes the git SHA, build timestamp, and a direct link to the matching commit on GitHub.\n2. Open the [Actions tab](https:\u002F\u002Fgithub.com\u002Fbhartiyashesh\u002Fpurelymailcalendar\u002Factions). Every deploy is a public build log tied to a commit.\n3. The same image is pushed to the public registry:\n   ```bash\n   docker pull ghcr.io\u002Fbhartiyashesh\u002Fpurelymailcalendar:latest\n   docker image inspect ghcr.io\u002Fbhartiyashesh\u002Fpurelymailcalendar:latest --format '{{ index .Config.Labels \"org.opencontainers.image.revision\" }}'\n   ```\n4. Compare the SHA shown in the footer of any signed-in page (or returned by `\u002Fapi\u002Fversion`) against the public commit history.\n5. Don't trust the deploy at all? Self-host:\n   ```bash\n   git clone https:\u002F\u002Fgithub.com\u002Fbhartiyashesh\u002Fpurelymailcalendar.git\n   cd purelymailcalendar\n   cp .env.compose.example .env  # fill in FERNET_KEY, SESSION_SECRET, RESEND_API_KEY\n   docker compose up -d\n   ```\n\nThe verification chain is: **public commit → public Actions build log → public image digest → `\u002Fapi\u002Fversion` on the running site → SHA badge in the footer**.\n\n## iTIP correctness notes\n\n- `ORGANIZER` and `ATTENDEE` use `mailto:` URIs.\n- The organizer is also added as an `ATTENDEE` with `PARTSTAT=ACCEPTED` and `RSVP=FALSE` — this makes Purelymail reliably show the event on the organizer's own calendar listing, and the organizer is filtered out of the SMTP recipient list so they don't get an invite to their own event.\n- New events use `SEQUENCE:0`; updates\u002Fcancels bump it. Replies don't bump `SEQUENCE`.\n- Updates and cancels reuse the original `UID`.\n- The MIME body is `multipart\u002Fmixed[ multipart\u002Falternative[ text\u002Fplain, text\u002Fcalendar; method=REQUEST ], application\u002Fics attachment ]`. Apple Mail keys off the inline `text\u002Fcalendar` part; Gmail keys off either; Outlook on Windows is happiest with the attachment too — that layout covers all three.\n- Stored CalDAV resources strip `METHOD` (RFC 4791 §4.1) — Purelymail rejects PUTs with `METHOD` even though the same blob is fine to send via SMTP.\n- Time zones are emitted with a minimal `VTIMEZONE` referencing the IANA TZID. Clients resolve it against their local zoneinfo.\n- The IMAP processor matches by `UID` and the responder's `mailto:` address; it won't write changes if `PARTSTAT` already matches.\n\n## License\n\nMIT. Made by [Yashesh Bharti](https:\u002F\u002Fyasheshbharti.com).\n","Purelymail Calendar 是一个为 Purelymail 邮箱用户提供的非官方日历和会议邀请 Web 应用，旨在弥补 Purelymail 自身在日历功能上的不足。该应用通过 FastAPI 和 React 技术栈构建，支持月、周、日视图，实现了基于 iTIP 标准的会议邀请发送与 RSVP 跟踪，并能自动同步参会者的回复状态。适用于需要通过邮件客户端（如 Apple Mail, Gmail, Outlook）发送专业会议邀请并追踪参与者响应情况的场景。此外，它采用魔法链接认证机制及端到端加密技术保护用户数据安全。",2,"2026-06-11 03:59:24","CREATED_QUERY"]