Calendar: calternal as a CalDAV server — an adapter over log entries plus connected calendars #41

Open
opened 2026-09-24 15:12:49 +00:00 by kayg · 21 comments
Owner

Owner decisions C2 + C6 (DESIGN §30): "calternal is not a calendar"; the ## 📝 Log entries in daily notes are authoritative. Build a CalDAV adapter so Apple Calendar, Fantastical, BusyCal etc. on the owner's devices can subscribe to calternal:

  • A Journal calendar that projects log entries as VEVENTs on the fly, without writing .ics files. UID derived from the log entry's ^block-id (mint one through calternal-notes-core when missing, without re-serializing untouched lines); SUMMARY = title, DTSTART/DTEND from the entry time/range in the zone it was logged in (C12); CATEGORIES from #tags; URL = note deeplink when the entry has a linked note; ETag = hash of the entry's canonical line; ctag / sync-token from the notes change stream.
  • Writes from CalDAV clients to the Journal calendar become log-entry edits in the right daily note (create → append a log entry; move/resize → change its time; delete → remove the line, recoverable via file versions), through the notes plugin (single writer, If-Match). Anything that cannot be represented as a log entry is rejected with a clear CalDAV error rather than silently changed.
  • The same CalDAV endpoint also presents the user's connected external calendars (proxying reads from the derived cache and writes to the provider), so one account on the phone shows everything.
  • Auth: installation tokens (bearer or app passwords generated per device in Settings → Account), data scope only.
  • Well-known discovery (/.well-known/caldav), principal and home-set per RFC 4791; test with Apple Calendar (via the macOS test machine when available), DAVx⁵-style clients, and the caldav-tester style conformance where feasible.
  • Built on dav-server + calcard; DAV semantics implemented in calternal (no mature Rust CalDAV server exists).

Context for the owning job

  • Repo: kayg/calternal (~/Developer/calternal). Read CLAUDE.md, CONTEXT.md and docs/DESIGN.md (§9, §17, §24, §25, §29, §30) first.
  • Vocabulary: a log entry is a retrospective - HH:MM[ - HH:MM] title #tags ^blockid line under ## 📝 Log in a daily note (Notes/Journal/YYYYMMDD-dailynote.md); an event is a scheduled calendar item from an external CalDAV provider. calternal is not a calendar store: log entries are authoritative for calternal's own time data; external events live at their provider.
  • calternal started as a lifelogging app and stays one; Calendar is the lifelog hub (everything by date). The mode tray defaults to Files, Calendar, Photos.
  • Owner rules: file over app; server is the single writer; data loss is unacceptable; performance first but never at the cost of finesse; UI copies Apple Calendar's structure (the owner shared macOS Calendar Week and Day screenshots: Day/Week/Month/Year segmented control top centre, large "September 2026" title with the month bold, ‹ Today › pager top right, all-day lane, hour grid with 09:00-style labels, red circle on today's date, event blocks with a coloured left rule and title/location/time lines, Day view with a mini month and an inspector on the right) plus Fantastical and BusyCal day/week ideas, rendered in the calternal.js design system (copy components verbatim, Claude reviews screenshots side by side); never ship sample/mock data; atomic commits; adversarial testing after API work; good enough, not perfect.
  • Comment on this issue when you start (branch, base SHA), on each finding, when blocked, and when finished (head SHA + gate output). Never close it.
Owner decisions C2 + C6 (DESIGN §30): "calternal is not a calendar"; the `## 📝 Log` entries in daily notes are authoritative. Build a **CalDAV adapter** so Apple Calendar, Fantastical, BusyCal etc. on the owner's devices can subscribe to calternal: - A **Journal** calendar that **projects log entries as VEVENTs on the fly, without writing .ics files**. UID derived from the log entry's `^block-id` (mint one through calternal-notes-core when missing, without re-serializing untouched lines); SUMMARY = title, DTSTART/DTEND from the entry time/range in the zone it was logged in (C12); CATEGORIES from #tags; URL = note deeplink when the entry has a linked note; ETag = hash of the entry's canonical line; ctag / sync-token from the notes change stream. - **Writes from CalDAV clients** to the Journal calendar become log-entry edits in the right daily note (create → append a log entry; move/resize → change its time; delete → remove the line, recoverable via file versions), through the notes plugin (single writer, If-Match). Anything that cannot be represented as a log entry is rejected with a clear CalDAV error rather than silently changed. - The same CalDAV endpoint also presents the user's **connected external calendars** (proxying reads from the derived cache and writes to the provider), so one account on the phone shows everything. - Auth: installation tokens (bearer or app passwords generated per device in Settings → Account), `data` scope only. - Well-known discovery (`/.well-known/caldav`), principal and home-set per RFC 4791; test with Apple Calendar (via the macOS test machine when available), DAVx⁵-style clients, and the `caldav-tester` style conformance where feasible. - Built on `dav-server` + `calcard`; DAV semantics implemented in calternal (no mature Rust CalDAV server exists). ## Context for the owning job - Repo: kayg/calternal (~/Developer/calternal). Read CLAUDE.md, CONTEXT.md and docs/DESIGN.md (§9, §17, §24, §25, §29, §30) first. - Vocabulary: a **log entry** is a retrospective `- HH:MM[ - HH:MM] title #tags ^blockid` line under `## 📝 Log` in a daily note (`Notes/Journal/YYYYMMDD-dailynote.md`); an **event** is a scheduled calendar item from an external CalDAV provider. calternal is **not** a calendar store: log entries are authoritative for calternal's own time data; external events live at their provider. - calternal started as a lifelogging app and stays one; Calendar is the lifelog hub (everything by date). The mode tray defaults to Files, Calendar, Photos. - Owner rules: file over app; server is the single writer; data loss is unacceptable; performance first but never at the cost of finesse; UI copies Apple Calendar's structure (the owner shared macOS Calendar Week and Day screenshots: Day/Week/Month/Year segmented control top centre, large "September 2026" title with the month bold, ‹ Today › pager top right, all-day lane, hour grid with 09:00-style labels, red circle on today's date, event blocks with a coloured left rule and title/location/time lines, Day view with a mini month and an inspector on the right) plus Fantastical and BusyCal day/week ideas, rendered in the calternal.js design system (copy components verbatim, Claude reviews screenshots side by side); never ship sample/mock data; atomic commits; adversarial testing after API work; good enough, not perfect. - Comment on this issue when you start (branch, base SHA), on each finding, when blocked, and when finished (head SHA + gate output). Never close it.
Author
Owner

Owner decision C13: yes, write-back is wanted ("this would be amazing"). Creating, moving or resizing a Journal event in Apple Calendar (or any CalDAV client) must rewrite or append the log line in the right daily note through the notes plugin (single writer, If-Match), recoverable via file versions. Edits that cannot be represented as a log line (attendees, alarms, recurrence) are rejected with a clear CalDAV error. DESIGN §30.

Owner decision C13: **yes, write-back is wanted** ("this would be amazing"). Creating, moving or resizing a Journal event in Apple Calendar (or any CalDAV client) must rewrite or append the log line in the right daily note through the notes plugin (single writer, If-Match), recoverable via file versions. Edits that cannot be represented as a log line (attendees, alarms, recurrence) are rejected with a clear CalDAV error. DESIGN §30.
Author
Owner

Started on branch job/caldav-server at base 63f3bdd6cb. Reading the issue and existing notes/server contracts before implementing the DAV adapter.

Started on branch job/caldav-server at base 63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f. Reading the issue and existing notes/server contracts before implementing the DAV adapter.
Author
Owner

Finding: the current Daily note Log grammar and notes-core parser carry HH:MM but no timezone field, and the notes index has no change cursor. The external calendar cache from #40 is also not on this branch. I will keep the Journal projection behind provider contracts and wire the external provider after #40 merges; I will not infer a timezone from the server locale or invent external calendar data.

Finding: the current Daily note Log grammar and notes-core parser carry HH:MM but no timezone field, and the notes index has no change cursor. The external calendar cache from #40 is also not on this branch. I will keep the Journal projection behind provider contracts and wire the external provider after #40 merges; I will not infer a timezone from the server locale or invent external calendar data.
Author
Owner

Implementation finding: the Journal write path can round trip same-day Log line edits through the Notes plugin, with file-versioned conditional replacement and a user lock. The live adversarial round returned ==== FINDINGS 0. Current constraints remain: the Daily note format has no per-entry timezone, #40's external calendar cache is absent on this branch, and the Notes index has no durable change stream for RFC 6578 sync tokens. Cross-day moves are rejected to avoid an unsafe two-file partial update.

Implementation finding: the Journal write path can round trip same-day Log line edits through the Notes plugin, with file-versioned conditional replacement and a user lock. The live adversarial round returned `==== FINDINGS 0`. Current constraints remain: the Daily note format has no per-entry timezone, #40's external calendar cache is absent on this branch, and the Notes index has no durable change stream for RFC 6578 sync tokens. Cross-day moves are rejected to avoid an unsafe two-file partial update.
Author
Owner

Finding: the requested ETag rule (hash only the canonical Log line) collides when a Log entry moves to another Daily note at the same clock time with unchanged title and tags. DTSTART changes but the ETag would not, so a CalDAV client could miss the move. I am including the Daily note date in the ETag input and recording that deviation for owner review; this is needed to avoid a sync collision.

Finding: the requested ETag rule (hash only the canonical Log line) collides when a Log entry moves to another Daily note at the same clock time with unchanged title and tags. DTSTART changes but the ETag would not, so a CalDAV client could miss the move. I am including the Daily note date in the ETag input and recording that deviation for owner review; this is needed to avoid a sync collision.
Author
Owner

Cross-date Journal moves now persist a two-note intent before writing either Daily note. Startup recovery replays it before requests; a regression test simulates a crash after the destination write and before the step record. The current live adversarial run, including cross-date PUT/GET/DELETE, ended with ==== FINDINGS 0.

Cross-date Journal moves now persist a two-note intent before writing either Daily note. Startup recovery replays it before requests; a regression test simulates a crash after the destination write and before the step record. The current live adversarial run, including cross-date PUT/GET/DELETE, ended with `==== FINDINGS 0`.
Author
Owner

Finished the branch implementation at 68847386df (job/caldav-server). Five atomic commits cover the Journal VEVENT projection, authenticated DAV routes, Notes write-back, linked Note URL projection, and recoverable cross-date moves. No push, merge, or deploy.

Final gates (verbatim output excerpts):

  • cargo fmt --check: no output; exit 0.
  • cargo clippy --all-targets -- -D warnings:
    Finished dev profile [unoptimized + debuginfo] target(s) in 29.17s
  • cargo test:
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.95s
    (35 test result lines in the complete log: 547 passed, 0 failed, 2 ignored; exit 0.)
  • bash packages/api-client/check-generated.sh:
    Running target/debug/calternal-server openapi
    $ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts
    ✨ openapi-typescript 7.13.0
    🚀 ../../contracts/openapi.json → src/generated.ts [323.2ms]
  • bash tests/adversarial/run.sh:
    server alive at end: True

==== FINDINGS 0

  • cargo clean:
    Removed 13521 files, 7.4GiB total

Remaining integration gaps: #40's external calendar cache is absent, so the external provider stub presents no external calendars; the Notes format has no per-entry timezone, and the Notes index has no durable change feed for RFC 6578 sync tokens. App passwords and Apple Calendar/DAVx⁵ device conformance require separate auth/device work. The current DAV routes use dav-server's method vocabulary with custom CalDAV semantics; a dav-server DavHandler backend is not yet integrated. These are not claimed as complete.

Owner review requested for the ETag deviation explained above (date plus canonical line), a persistent timezone field for Log entries, and whether content-hash ctags are acceptable until a durable Notes change stream exists. The issue remains open.

Finished the branch implementation at 68847386dfa7d95b4c3a04b27db249243c00a843 (job/caldav-server). Five atomic commits cover the Journal VEVENT projection, authenticated DAV routes, Notes write-back, linked Note URL projection, and recoverable cross-date moves. No push, merge, or deploy. Final gates (verbatim output excerpts): - `cargo fmt --check`: no output; exit 0. - `cargo clippy --all-targets -- -D warnings`: Finished `dev` profile [unoptimized + debuginfo] target(s) in 29.17s - `cargo test`: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.95s (35 test result lines in the complete log: 547 passed, 0 failed, 2 ignored; exit 0.) - `bash packages/api-client/check-generated.sh`: Running `target/debug/calternal-server openapi` $ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts ✨ openapi-typescript 7.13.0 🚀 ../../contracts/openapi.json → src/generated.ts [323.2ms] - `bash tests/adversarial/run.sh`: server alive at end: True ==== FINDINGS 0 - `cargo clean`: Removed 13521 files, 7.4GiB total Remaining integration gaps: #40's external calendar cache is absent, so the external provider stub presents no external calendars; the Notes format has no per-entry timezone, and the Notes index has no durable change feed for RFC 6578 sync tokens. App passwords and Apple Calendar/DAVx⁵ device conformance require separate auth/device work. The current DAV routes use dav-server's method vocabulary with custom CalDAV semantics; a dav-server DavHandler backend is not yet integrated. These are not claimed as complete. Owner review requested for the ETag deviation explained above (date plus canonical line), a persistent timezone field for Log entries, and whether content-hash ctags are acceptable until a durable Notes change stream exists. The issue remains open.
Author
Owner

Merged the Journal CalDAV slice into main (job/caldav-server @ 6884738): Log entries projected as VEVENTs, authenticated DAV discovery, writes through the Notes plugin with ETag preconditions, recovery of interrupted cross-date moves; 0 adversarial findings.
Orchestrator decisions on the job's questions: including the daily-note date in the ETag is accepted. Floating times are acceptable only until per-entry zones exist.
Remaining for this issue (next job): (1) per-entry time zone recording in log entries (DESIGN §30 C12) and emitting TZID/UTC instead of floating times; (2) a durable Notes change feed for RFC 6578 sync-collection and a real ctag/sync-token; (3) the ExternalCalendarsProvider wired to #40's cache once it lands; (4) per-device app passwords (Settings → Account) for CalDAV clients that cannot send bearer tokens; (5) conformance testing with real clients (DAVx⁵-style and Apple Calendar via the macOS machine, #24).

Merged the Journal CalDAV slice into main (job/caldav-server @ 6884738): Log entries projected as VEVENTs, authenticated DAV discovery, writes through the Notes plugin with ETag preconditions, recovery of interrupted cross-date moves; 0 adversarial findings. Orchestrator decisions on the job's questions: including the daily-note date in the ETag is accepted. Floating times are acceptable only until per-entry zones exist. Remaining for this issue (next job): (1) per-entry time zone recording in log entries (DESIGN §30 C12) and emitting TZID/UTC instead of floating times; (2) a durable Notes change feed for RFC 6578 sync-collection and a real ctag/sync-token; (3) the ExternalCalendarsProvider wired to #40's cache once it lands; (4) per-device app passwords (Settings → Account) for CalDAV clients that cannot send bearer tokens; (5) conformance testing with real clients (DAVx⁵-style and Apple Calendar via the macOS machine, #24).
Author
Owner

Started continuation on branch job/caldav-server-2 at base cc18cf16aa. Scope: per-entry Log time zones and TZID/UTC projection; durable Notes changes with RFC 6578 sync tokens and ctag; data-scope per-installation app passwords for DAV Basic auth. External calendar integration and real-device conformance remain with #40/#24. I am inspecting the merged Journal paths before editing.

Started continuation on branch job/caldav-server-2 at base cc18cf16aa36e88f8aedad6d7ad7673e53ad1326. Scope: per-entry Log time zones and TZID/UTC projection; durable Notes changes with RFC 6578 sync tokens and ctag; data-scope per-installation app passwords for DAV Basic auth. External calendar integration and real-device conformance remain with #40/#24. I am inspecting the merged Journal paths before editing.
Author
Owner

Finding: cross-date Journal move recovery writes the destination before removing the source. A durable feed keyed only by block ID would collide during replay; the Notes projection therefore keys its transient rows by (user, Daily note path, block ID), then coalesces sync results against the final live resource state. The feed cursor and rows are read from one SQLite snapshot. The Markdown Daily note remains authoritative.

Finding: cross-date Journal move recovery writes the destination before removing the source. A durable feed keyed only by block ID would collide during replay; the Notes projection therefore keys its transient rows by (user, Daily note path, block ID), then coalesces sync results against the final live resource state. The feed cursor and rows are read from one SQLite snapshot. The Markdown Daily note remains authoritative.
Author
Owner

Finding: a cursor containing only SQLite's sequence number can collide after the rebuildable Notes index is recreated. A prior device could present the same number and miss changes. I am adding a per-index epoch to the opaque sync token so a rebuilt index rejects old tokens and forces a full sync.

Finding: a cursor containing only SQLite's sequence number can collide after the rebuildable Notes index is recreated. A prior device could present the same number and miss changes. I am adding a per-index epoch to the opaque sync token so a rebuilt index rejects old tokens and forces a full sync.
Author
Owner

Finding: Notes changed through Files or a restored home were not indexed by the existing Root change bridge; only an hourly Notes reconcile would reach the Journal feed. I added single-Note adoption on Root and home-watcher changes, plus a full Notes reconcile when the Root broadcast lags. Incremental sync now queries a durable SQLite change table and only opens Daily notes that still lack block IDs.

Finding: Notes changed through Files or a restored home were not indexed by the existing Root change bridge; only an hourly Notes reconcile would reach the Journal feed. I added single-Note adoption on Root and home-watcher changes, plus a full Notes reconcile when the Root broadcast lags. Incremental sync now queries a durable SQLite change table and only opens Daily notes that still lack block IDs.
Author
Owner

Finding: a global SQLite change sequence would expose other users' activity through gaps in a user's sync token. The feed now allocates a sequence per user and binds each opaque epoch to that user. A regression test gives two users the same block ID and checks that their cursors stay independent and a token from one is rejected for the other.

Finding: a global SQLite change sequence would expose other users' activity through gaps in a user's sync token. The feed now allocates a sequence per user and binds each opaque epoch to that user. A regression test gives two users the same block ID and checks that their cursors stay independent and a token from one is rejected for the other.
Author
Owner

Completed issue #41 scope (1), (2), and (4) on branch job/caldav-server-2.
Head: 039da0cbd4a493519bf50845c3644a20ba84e9da.

  • Journal Log entries record an IANA zone; CalDAV emits TZID local values or UTC Z values and rejects invalid/ambiguous/nonexistent local writes.
  • Notes has a durable per-user Journal change feed. CalDAV ctag and sync-token use it, and sync-collection returns initial/delta changes and tombstones.
  • Auth stores named per-device app passwords as Argon2id hashes; list/create/revoke API and DAV-only HTTP Basic authentication are wired. Fresh account assertion gates creation and revocation.
  • OpenAPI, generated client, and live DAV adversarial probes updated.

Gate output (verbatim excerpts):

$ cargo fmt --check
(no output; exit 0)
$ cargo clippy --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.73s
$ cargo test
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 12s
test result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 28.63s
test result: ok. 356 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.18s
test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.12s
$ bash packages/api-client/check-generated.sh
     Running `target/debug/calternal-server openapi`
$ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts
✨ openapi-typescript 7.13.0
🚀 ../../contracts/openapi.json → src/generated.ts [267.7ms]
$ bun run --cwd apps/web check
svelte-check found 0 errors and 0 warnings
$ bun run --cwd apps/web test
      Tests  9 passed (9)
$ bash tests/adversarial/run.sh
server alive at end: True

==== FINDINGS 0
$ cargo clean
     Removed 16978 files, 8.2GiB total

Known gaps: external calendar providers (#40) and Apple/DAVx⁵ device conformance (#24) were explicitly deferred. Account settings UI for app passwords is not in this job scope. Legacy Log lines without a zone still project as floating times because their original zone is unknown.

Owner confirmation requested for implementation choices outside explicit design text: [tz=IANA] Log line marker; rejection of DST fold/gap local values instead of choosing an offset; no VTIMEZONE component in TZID output pending device conformance. The zone marker records a zone but no fold/offset bit, so a composer entry in a repeated DST hour is not uniquely instant-resolved.

No push, merge, or deployment was performed. Worktree is clean.

Completed issue #41 scope (1), (2), and (4) on branch `job/caldav-server-2`. Head: `039da0cbd4a493519bf50845c3644a20ba84e9da`. - Journal Log entries record an IANA zone; CalDAV emits TZID local values or UTC Z values and rejects invalid/ambiguous/nonexistent local writes. - Notes has a durable per-user Journal change feed. CalDAV ctag and sync-token use it, and sync-collection returns initial/delta changes and tombstones. - Auth stores named per-device app passwords as Argon2id hashes; list/create/revoke API and DAV-only HTTP Basic authentication are wired. Fresh account assertion gates creation and revocation. - OpenAPI, generated client, and live DAV adversarial probes updated. Gate output (verbatim excerpts): ``` $ cargo fmt --check (no output; exit 0) $ cargo clippy --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.73s $ cargo test Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 12s test result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 28.63s test result: ok. 356 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.18s test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.12s $ bash packages/api-client/check-generated.sh Running `target/debug/calternal-server openapi` $ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts ✨ openapi-typescript 7.13.0 🚀 ../../contracts/openapi.json → src/generated.ts [267.7ms] $ bun run --cwd apps/web check svelte-check found 0 errors and 0 warnings $ bun run --cwd apps/web test Tests 9 passed (9) $ bash tests/adversarial/run.sh server alive at end: True ==== FINDINGS 0 $ cargo clean Removed 16978 files, 8.2GiB total ``` Known gaps: external calendar providers (#40) and Apple/DAVx⁵ device conformance (#24) were explicitly deferred. Account settings UI for app passwords is not in this job scope. Legacy Log lines without a zone still project as floating times because their original zone is unknown. Owner confirmation requested for implementation choices outside explicit design text: `[tz=IANA]` Log line marker; rejection of DST fold/gap local values instead of choosing an offset; no VTIMEZONE component in TZID output pending device conformance. The zone marker records a zone but no fold/offset bit, so a composer entry in a repeated DST hour is not uniquely instant-resolved. No push, merge, or deployment was performed. Worktree is clean.
Author
Owner

The 2026-09-28 local CalDAV probe sent a Journal PUT with If-Match using a valid VEVENT plus a malformed VALARM containing ACTION:DISPLAY but no TRIGGER. The server returned HTTP 201 with an empty body. The adversarial probe expects malformed calendar data to be rejected. This is an observed validation gap in the CalDAV adapter; no CalDAV code changed in this job.

The 2026-09-28 local CalDAV probe sent a Journal PUT with If-Match using a valid VEVENT plus a malformed VALARM containing ACTION:DISPLAY but no TRIGGER. The server returned HTTP 201 with an empty body. The adversarial probe expects malformed calendar data to be rejected. This is an observed validation gap in the CalDAV adapter; no CalDAV code changed in this job.
Author
Owner

During the single real-server adversarial round for #291, the DAV write probe's malformed alarm case was accepted: a PUT containing BEGIN:VALARM, ACTION:DISPLAY, END:VALARM with no TRIGGER returned HTTP 201. tests/adversarial/attack.py marks any 2xx for this case as an accepted-hostile-input finding (bad_if_2xx=True). This is tracked here because the Journal DAV adapter must reject values it cannot represent. The run did not assess what data was persisted from the alarm.

During the single real-server adversarial round for #291, the DAV write probe's malformed alarm case was accepted: a `PUT` containing `BEGIN:VALARM`, `ACTION:DISPLAY`, `END:VALARM` with no `TRIGGER` returned HTTP 201. `tests/adversarial/attack.py` marks any 2xx for this case as an accepted-hostile-input finding (`bad_if_2xx=True`). This is tracked here because the Journal DAV adapter must reject values it cannot represent. The run did not assess what data was persisted from the alarm.
Author
Owner

Additional adversarial evidence from the #188 run at HEAD 9bd81553: an authenticated PROPFIND with Depth: 1 on the DAV home returned HTTP 207, but its XML body did not contain calendar-home-set. The existing probe records this as DAV discovery :: unexpected response 207. The same run was under heavy fixture load; this note records the observed response without assigning a cause.

Additional adversarial evidence from the #188 run at HEAD 9bd81553: an authenticated `PROPFIND` with `Depth: 1` on the DAV home returned HTTP 207, but its XML body did not contain `calendar-home-set`. The existing probe records this as `DAV discovery :: unexpected response 207`. The same run was under heavy fixture load; this note records the observed response without assigning a cause.
Author
Owner

The 2026-09-30 local real-server adversarial run found a CalDAV probe expectation mismatch on merged origin/dev (e96a8bf2a). The probe first created a Journal resource, then PUT the existing resource with a valid VALARM and its current If-Match ETag. The update returned 204. The probe expects 201 and only refreshes its saved ETag after 201, so its following cross-date PUT and DELETE use the stale ETag and return 412.

I left the existing expectation unchanged per the owner rule. Please review the probe's create-versus-update status expectation and update its ETag handling only after confirming the intended behavior. This report does not classify the 412 responses as server defects.

The 2026-09-30 local real-server adversarial run found a CalDAV probe expectation mismatch on merged `origin/dev` (`e96a8bf2a`). The probe first created a Journal resource, then PUT the existing resource with a valid VALARM and its current `If-Match` ETag. The update returned 204. The probe expects 201 and only refreshes its saved ETag after 201, so its following cross-date PUT and DELETE use the stale ETag and return 412. I left the existing expectation unchanged per the owner rule. Please review the probe's create-versus-update status expectation and update its ETag handling only after confirming the intended behavior. This report does not classify the 412 responses as server defects.
Author
Owner

Hygiene review: reports still show a malformed VALARM accepted with HTTP 201 and a DAV home PROPFIND missing calendar-home-set. The later 204 update response also leaves the probe using a stale ETag. These remain unresolved, so #41 stays open.

Hygiene review: reports still show a malformed VALARM accepted with HTTP 201 and a DAV home PROPFIND missing `calendar-home-set`. The later 204 update response also leaves the probe using a stale ETag. These remain unresolved, so #41 stays open.
Author
Owner

Stale calendar shape: this issue projects all Log entries into one Journal calendar. The later owner decision in DESIGN §46 (2026-09-28, Area calendars over CalDAV) says the adapter presents one calendar per #area Tag, not one Journal calendar. Recommend replacing the old Journal-calendar acceptance here with §46 and keeping only the CalDAV work that still matches that design. Do not close this issue in the audit.

Stale calendar shape: this issue projects all Log entries into one Journal calendar. The later owner decision in DESIGN §46 (2026-09-28, Area calendars over CalDAV) says the adapter presents one calendar per #area Tag, not one Journal calendar. Recommend replacing the old Journal-calendar acceptance here with §46 and keeping only the CalDAV work that still matches that design. Do not close this issue in the audit.
Author
Owner

DESIGN §46 replaces the old single-Journal-calendar shape with one CalDAV calendar per #area Tag. The remaining Calendar and provider work is not all verified on origin/dev, so this issue stays open for the matching implementation.

DESIGN §46 replaces the old single-Journal-calendar shape with one CalDAV calendar per `#area` Tag. The remaining Calendar and provider work is not all verified on origin/dev, so this issue stays open for the matching implementation.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#41
No description provided.