npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

n8n-nodes-icloud-caldav

v0.3.1

Published

n8n community node for CalDAV (RFC 4791) calendars — iCloud-first, HTTP Basic auth

Readme

n8n-nodes-caldav

CalDAV node for n8n: essential calendar operations against any RFC 4791–compliant server, with Apple iCloud supported first-class. Authentication is plain HTTP Basic (username + password / app-specific password) — no OAuth, no vendor SDK. The node speaks PROPFIND/REPORT/PUT/DELETE directly and builds/parses iCalendar itself; the only runtime dependency is fast-xml-parser.

Also works with Fastmail, Nextcloud, Radicale, SOGo, Synology, and other RFC 4791 servers.

Naming. The plain n8n-nodes-caldav is taken on npm, so this is published as n8n-nodes-icloud-caldav (an unscoped name — n8n derives a node's type from packageName.nodeName, so a dot in the package name breaks type resolution). Minimal and auditable by design, because it holds an iCloud app-specific password. Self-hosted n8n only.

Install (self-hosted n8n)

Simplest — n8n UI: Settings → Community NodesInstalln8n-nodes-icloud-caldav (requires N8N_COMMUNITY_PACKAGES_ENABLED=true, the default on self-hosted).

Custom-extensions folder (from npm):

mkdir -p ~/.n8n/custom && cd ~/.n8n/custom
npm init -y            # once, if there's no package.json yet
npm install n8n-nodes-icloud-caldav
# Restart n8n. The node appears as "CalDAV".

Or point N8N_CUSTOM_EXTENSIONS at that directory.

Docker: install into the mounted ~/.n8n volume, or bake it into a custom image:

FROM docker.n8n.io/n8nio/n8n:latest
USER root
RUN mkdir -p /home/node/.n8n/custom && cd /home/node/.n8n/custom \
 && (npm init -y || true) && npm install n8n-nodes-icloud-caldav \
 && chown -R node:node /home/node/.n8n
USER node

From source (development): npm install && npm run build, then npm link it into ~/.n8n/custom.

iCloud setup

  • Server URL: https://caldav.icloud.com
  • Username: your Apple ID email address
  • Password: an app-specific password. Generate one at account.apple.com → Sign-In and Security → App-Specific Passwords. Never use your main Apple ID password — with two-factor enabled it will not work, and you should not hand it to anything anyway.

After authentication iCloud points at a partition host (https://pXX-caldav.icloud.com/<dsid>/…). The node discovers principal and calendar-home URLs at runtime and follows them verbatim — no hardcoded paths.

Credentials (CalDAV API)

| Field | Default | Notes | | ---------- | --------------------------- | ----------------------------------------------------------- | | Server URL | https://caldav.icloud.com | Base URL of the CalDAV server | | Username | — | For iCloud: Apple ID email | | Password | — | For iCloud: app-specific password — never the main password |

The credential Test runs a PROPFIND for current-user-principal and passes on 207 Multi-Status, so a green check means the server URL and login actually work.

Operations

Calendar → Get Many

Runs the discovery cascade (principal → calendar-home → PROPFIND Depth: 1) and returns each VEVENT-capable calendar: displayName, url (collection href), ctag, and supported components. This also feeds the calendar dropdown used by all event operations.

Event → Create

Fields: calendar (dropdown), summary, start, end, allDay, and reminders (DISPLAY alarms, minutes before — defaults to one 15-minute reminder). description, location, timezone (IANA), rrule, and the optional uid override (idempotent creates) live under Additional Fields. The event is PUT with If-None-Match: * — create-only, it can never overwrite an existing object. Returns uid, href, and etag.

Example — a bill-due reminder: summary Pay electricity, start 2026-08-01T09:00:00Z, end 2026-08-01T09:15:00Z, reminder 15 → timed event with a 15-minutes-before popup alarm.

Event → Get

By href (direct GET) or by uid (calendar-query REPORT with an exact, case-sensitive i;octet match). Returns parsed fields plus etag and the raw ICS.

Event → Get Many

calendar-query REPORT on a calendar with an optional start/end time-range filter. Returns one item per event: parsed fields, href, etag, raw ICS.

Example — this week's events: start 2026-07-06T00:00:00Z, end 2026-07-13T00:00:00Z.

Event → Update (ETag / If-Match contract)

CalDAV has no partial update — a PUT replaces the whole object (RFC 4791 §8.2.2). Update therefore: fetches the current ICS from href, patches only the fields you set (attendees, extra alarms, X- properties, VTIMEZONE survive untouched), and PUTs it back with If-Match: <etag> using the etag you pass in — the one you got from Create/Get/Get Many. If someone changed the event in the meantime, the server answers 412 Precondition Failed instead of silently overwriting: re-fetch the event (fresh etag), reapply your change, retry. The ETag field is optional — leaving it empty uses the freshly fetched ETag instead (your fields still win, but concurrent-edit detection is weaker), so pass it when you have it.

Example — retitle an event: pass href + etag from a previous Get, set summary Pay electricity (paid).

Event → Delete

DELETE on the event href, with If-Match when you supply the etag. Deleting an event that is already gone (404) counts as success — deletes are idempotent and safe to retry. Enable Fail on Missing if you want a 404 to error instead.

Timezone behavior

  • Default: UTC. Start/end are converted to DTSTART:…Z — always valid, on every server.
  • With timezone set (e.g. America/Los_Angeles) the event is written as DTSTART;TZID=…:… without an embedded VTIMEZONE component. Servers implementing RFC 7809 (time zones by reference — iCloud among them) accept this. Known limitation: strict RFC 5545 servers that insist on an inline VTIMEZONE may reject such events — use UTC there. The node never generates VTIMEZONE definitions.
  • A naive start like 2026-07-06T09:00:00 with timezone set means wall-clock time in that zone; a Z/offset timestamp is converted into it.
  • All-day events use date-only values (YYYY-MM-DD); end is the inclusive last day (the node emits RFC 5545's exclusive DTEND internally and maps it back on read).

Recurrence (RRULE)

rrule is passed through verbatim as a raw RFC 5545 value, e.g. FREQ=MONTHLY;BYMONTHDAY=1. Combine it with timezone so occurrences stay on local wall time across DST changes.

Tests

Unit (offline, no credentials):

npm run test:unit

Covers the pure parts: ICS building/parsing (escaping, 75-octet folding, all-day vs timed, VALARM) and the XML/href-resolution helpers.

End-to-end (real server, real account):

CALDAV_SERVER=https://caldav.icloud.com \
[email protected] \
CALDAV_PASSWORD=your-app-specific-password \
CALDAV_TEST_CALENDAR="Some Calendar" \
npm run test:e2e
  • CALDAV_TEST_CALENDAR (optional) picks the target calendar by display name; otherwise the first VEVENT-capable calendar is used. Prefer a scratch calendar.
  • Exercises the real protocol modules through seven stages: auth (207), discovery, list calendars, create (a timed and an all-day event, each with a 15-minute reminder), read-back (time-range REPORT + UID-filtered REPORT), update (If-Match, then a deliberate stale-ETag PUT that must fail with 412), delete (then a repeat DELETE that must return 404).
  • Creates only events titled n8n-caldav e2e … (safe to delete) and deletes them afterwards — cleanup runs even when a stage fails. The password / Authorization header is never printed.
  • For iCloud this needs a real account with an app-specific password (see iCloud setup).

Second-server run (Radicale in Docker) to prove generality beyond iCloud:

docker run --rm -p 5232:5232 python:3.12-alpine sh -c '
  pip install --quiet radicale &&
  printf "e2e:e2e-password\n" > /tmp/users &&
  exec python -m radicale --server-hosts 0.0.0.0:5232 \
    --auth-type htpasswd --auth-htpasswd-filename /tmp/users \
    --auth-htpasswd-encryption plain'

A fresh Radicale user has no calendars: open http://localhost:5232, log in as e2e / e2e-password, create a calendar in the built-in web UI, then:

CALDAV_SERVER=http://localhost:5232 \
CALDAV_USERNAME=e2e \
CALDAV_PASSWORD=e2e-password \
npm run test:e2e

Troubleshooting

| Symptom | Likely cause / fix | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | 401 Unauthorized | iCloud: you used the Apple ID password — generate an app-specific password at account.apple.com. Also check the username is the full email. | | No calendars found | Wrong server URL (scheme, host, or a stray path/trailing slash), or the account has no VEVENT calendars. iCloud: exactly https://caldav.icloud.com. | | 412 Precondition Failed | ETag conflict — the event changed since you fetched it. Re-fetch to get the current etag and retry. On Create it means the object already exists. | | 507 Insufficient Storage | Server-side quota exceeded — free up space in the account. |

License

MIT — © 2026 Howie Lin.