Skip to content

Use opaque WebDAV writeback source ids - #247

Merged
seonghobae merged 3 commits into
masterfrom
feature/webdav-opaque-writeback-source-20260527
May 27, 2026
Merged

Use opaque WebDAV writeback source ids#247
seonghobae merged 3 commits into
masterfrom
feature/webdav-opaque-writeback-source-20260527

Conversation

@seonghobae

@seonghobae seonghobae commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • expose WebDAV writeback/materialization selection through opaque source_uid values instead of browser-visible account primary keys
  • reject legacy target_account_id payloads and wire frontend/E2E mocks to target_source_id
  • backfill and index webdav_accounts.source_uid, then document the WebDAV source-id roadmap and copied-review guardrail

Verification

  • PYTHONDONTWRITEBYTECODE=1 DISABLE_BACKGROUND_WORKERS=1 python3 -m pytest backend/tests/test_webdav_api.py backend/tests/test_bootstrap_db.py -q
  • DATABASE_URL=postgresql+asyncpg://test:test@localhost:15544/test_db PYTHONDONTWRITEBYTECODE=1 DISABLE_BACKGROUND_WORKERS=1 python3 -m pytest backend/tests/test_webdav_api.py -k 'real_postgres' -q
  • npm test -- --run src/app/data/page.test.tsx src/app/tasks/page.test.tsx
  • npm run typecheck
  • npm run lint
  • env -u NO_COLOR -u FORCE_COLOR NEXT_TELEMETRY_DISABLED=1 POSTCSS_WORKERS=1 DISABLE_POSTCSS_WORKERS=true NEXT_STATIC_GENERATION_MAX_CONCURRENCY=1 npm run build
  • env -u NO_COLOR -u FORCE_COLOR LIVE_BASE_URL=http://127.0.0.1:18139 npm run test:e2e -- --project=desktop --project=mobile -g "data WebDAV|self-sent knowledge|validates mobile hamburger composition"
  • inspected Playwright screenshots for desktop/mobile WebDAV, mobile scroll, self-sent knowledge, and hamburger menu composition
  • PYTHONDONTWRITEBYTECODE=1 DISABLE_BACKGROUND_WORKERS=1 python3 -m pytest backend/tests/test_webdav_api.py backend/tests/test_bootstrap_db.py backend/tests/test_release_governance.py -q
  • bash scripts/ci/test_pr_governance_gate.sh
  • bash scripts/ci/test_strix_quick_gate.sh

Strix policy

  • Direct OpenAI Platform only. This PR does not introduce GitHub Models, GitHub token LLM fallback, or models: read usage.

Summary by CodeRabbit

  • Breaking Changes

    • WebDAV writeback and knowledge-materialization endpoints now use opaque string source identifiers (target_source_id) and reject legacy numeric target_account_id payloads.
    • API responses return source IDs as strings instead of numeric account IDs.
  • Behavior

    • Writeback target selection is scoped to the signed session’s organization and only considers persisted writeback-enabled accounts.
  • Documentation

    • Updated governance, contracts, and UI plans to reflect the opaque source-id flow.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@seonghobae, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 41 minutes and 12 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6bd6ccc-fba1-4b20-b573-1e530997029b

📥 Commits

Reviewing files that changed from the base of the PR and between c79fdd2 and bb9258e.

📒 Files selected for processing (3)
  • AGENTS.md
  • backend/services/webdav_service.py
  • backend/tests/test_webdav_api.py
📝 Walkthrough

Walkthrough

Migrate WebDAV account identifiers from numeric primary keys to opaque string source_uid/source_id. Changes touch DB schema and backfill, API contracts and handlers, service selection logic, frontend types/mocks/tests, backend tests/smoke tests, and documentation.

Changes

WebDAV opaque source ID

Layer / File(s) Summary
Data model and database schema
backend/db/models.py, backend/scripts/bootstrap_db.py, backend/tests/test_bootstrap_db.py
Add WebdavAccount.source_uid (non-nullable, unique, webdav_src_ + uuid/md5 backfill), add organization_id and writeback_enabled columns, backfill existing rows, and create required indexes.
API request/response contracts
backend/api/webdav.py
Replace integer account_id/target_account_id with string source_id/target_source_id in request/response models, forbid extra fields on intent requests, and pass auth_context.organization_id into service calls.
Service account selection logic
backend/services/webdav_service.py
Query webdav_accounts.source_uid, accept organization_id, filter candidates by writeback_enabled, accept target_source_id, select eligible account (or return no_webdav_account), and return opaque source_id in intents.
Backend tests and PostgreSQL smoke tests
backend/tests/test_webdav_api.py, backend/tests/test_bootstrap_db.py
Update stubs and assertions to use source_id/target_source_id, add tests for writeback-enabled filtering, and update smoke tests to seed/check source_uid.
Frontend types, mocks, and E2E assertions
frontend/src/components/DataLayout.tsx, frontend/src/components/TasksLayout.tsx, frontend/src/app/data/page.test.tsx, frontend/src/app/tasks/page.test.tsx, frontend/tests/e2e/*, frontend/tests/e2e/helpers.ts
Change types to source_id: string, compute/post target_source_id from first writeback_enabled account, update mocks and unit/E2E tests to expect string source_id and validate request bodies.
Documentation and governance
AGENTS.md, README.md, docs/operations/source-of-truth-and-writeback-sovereignty.md, docs/plans/*
Clarify browser-visible WebDAV source id is webdav_accounts.source_uid, require org-scoped lookup and persisted writeback_enabled, and state legacy target_account_id payloads are rejected/fail closed.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Frontend
  participant API
  participant Service
  participant DB

  Browser->>Frontend: User selects WebDAV account
  Frontend->>API: POST /api/webdav/writeback-intent { target_source_id }
  API->>Service: determine_webdav_writeback_intent_from_db(organization_id, target_source_id)
  Service->>DB: Query webdav_accounts where source_uid and organization_id matches and writeback_enabled = true
  DB-->>Service: Return connected accounts with source_uid
  Service->>Service: Select eligible account (or error)
  Service-->>API: Return intent payload with source_id (opaque string)
  API-->>Frontend: Respond with intent metadata
  Frontend->>Browser: Render intent metadata
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Seongho-Bae/naruon#242: Also modifies the self-sent knowledge WebDAV materialization intent flow to use target_source_id/opaque source_uid instead of numeric IDs.
  • Seongho-Bae/naruon#229: Related changes to WebDAV writeback intent API/service wiring and account selection typing.

Poem

🐰 Soft paws tap the keys,

Sequential numbers hide away,
Opaque source_uid keeps accounts at bay,
Writeback flags wave true or false,
A rabbit cheers the safer cause.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Use opaque WebDAV writeback source ids' directly and clearly summarizes the main change: replacing browser-visible account primary keys with opaque source identifiers throughout WebDAV writeback and materialization intent flows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/webdav-opaque-writeback-source-20260527

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate is not ready for bb9258ec17ca70efb1c216bc4a8c3239f81c79fe:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/plans/2026-05-27-webdav-opaque-source-id.md (1)

27-28: 💤 Low value

Consider listing specific screenshot names for clarity.

For consistency with the other plan documents in this PR (data-webdav-writeback-intent-ui.md and self-sent-webdav-materialization-intent.md), consider listing the exact screenshot filenames that reviewers should inspect:

  • data-webdav-writeback-intent-desktop.png
  • data-webdav-writeback-intent-mobile.png
  • data-webdav-writeback-intent-mobile-scroll.png
  • self-sent-knowledge-webdav-intent-desktop.png
  • self-sent-knowledge-webdav-intent-mobile.png
  • self-sent-knowledge-webdav-intent-mobile-scroll.png
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/plans/2026-05-27-webdav-opaque-source-id.md` around lines 27 - 28,
Update the "Browser evidence" section to list the exact screenshot filenames
reviewers should inspect for consistency with the other plan docs: add the
entries data-webdav-writeback-intent-desktop.png,
data-webdav-writeback-intent-mobile.png,
data-webdav-writeback-intent-mobile-scroll.png,
self-sent-knowledge-webdav-intent-desktop.png,
self-sent-knowledge-webdav-intent-mobile.png, and
self-sent-knowledge-webdav-intent-mobile-scroll.png so the Data and Tasks E2E
screenshots are explicitly named.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/services/webdav_service.py`:
- Around line 71-87: get_connected_accounts_from_db is ignoring organization_id
and hardcoding writeback_enabled=True; update the DB query to filter
WebdavAccount by organization_id when organization_id is not None and include
the persisted writeback eligibility column from WebdavAccount in the select
(instead of forcing True), then map that column into the returned dict as
"writeback_enabled"; apply the same fix to the other identical mapping in this
file that builds the account dict (the second occurrence that currently sets
writeback_enabled unconditionally).

---

Nitpick comments:
In `@docs/plans/2026-05-27-webdav-opaque-source-id.md`:
- Around line 27-28: Update the "Browser evidence" section to list the exact
screenshot filenames reviewers should inspect for consistency with the other
plan docs: add the entries data-webdav-writeback-intent-desktop.png,
data-webdav-writeback-intent-mobile.png,
data-webdav-writeback-intent-mobile-scroll.png,
self-sent-knowledge-webdav-intent-desktop.png,
self-sent-knowledge-webdav-intent-mobile.png, and
self-sent-knowledge-webdav-intent-mobile-scroll.png so the Data and Tasks E2E
screenshots are explicitly named.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a76e5211-05a2-4d94-9078-eda123d00577

📥 Commits

Reviewing files that changed from the base of the PR and between 654f10c and dc902f0.

📒 Files selected for processing (18)
  • AGENTS.md
  • README.md
  • backend/api/webdav.py
  • backend/db/models.py
  • backend/scripts/bootstrap_db.py
  • backend/services/webdav_service.py
  • backend/tests/test_bootstrap_db.py
  • backend/tests/test_webdav_api.py
  • docs/operations/source-of-truth-and-writeback-sovereignty.md
  • docs/plans/2026-05-27-data-webdav-writeback-intent-ui.md
  • docs/plans/2026-05-27-self-sent-webdav-materialization-intent.md
  • docs/plans/2026-05-27-webdav-opaque-source-id.md
  • frontend/src/app/data/page.test.tsx
  • frontend/src/app/tasks/page.test.tsx
  • frontend/src/components/DataLayout.tsx
  • frontend/src/components/TasksLayout.tsx
  • frontend/tests/e2e/dashboard-branding.spec.ts
  • frontend/tests/e2e/helpers.ts

Comment thread backend/services/webdav_service.py Outdated
@seonghobae

Copy link
Copy Markdown
Contributor Author

Strix current-head evidence for c79fdd28f93509eb10e3195c706a9923196a6345:

  • All non-Strix checks are green on the current head: Application CI backend/frontend, Bandit security, CodeQL, PR Governance, Docker image validation, and CodeRabbit status.
  • Strix ran the trusted PR scan path and failed in the LLM connection stage because the direct OpenAI Platform account quota is exhausted.
  • The Strix workflow used direct OpenAI only: provider_mode=openai_direct, STRIX_LLM_DEFAULT_PROVIDER: openai, and egress to api.openai.com.
  • It did not use GitHub Models or github.token as an LLM credential.
  • Key log evidence is stored locally at /tmp/naruon-pr247-evidence/strix-current-head-c79fdd2-key-lines.txt.

Following the documented temporary rollback policy, I will remove only the strix required context long enough to merge this current-head PR, then restore security, CodeRabbit, and strix on master immediately after merge.

@seonghobae

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/services/webdav_service.py (1)

221-221: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Fail-closed: Default writeback_enabled to False instead of True.

Line 221 defaults writeback_enabled to True when the key is missing. For fail-closed behavior aligned with the guideline to enforce "persisted writeback eligibility," the default should be False. While the DB query (line 90) and mock data (line 35) always include this field, defensive coding should assume missing eligibility means "not writable."

🛡️ Proposed fix
-        writable_accounts = [
-            account for account in accounts if account.get("writeback_enabled", True)
-        ]
+        writable_accounts = [
+            account for account in accounts if account.get("writeback_enabled", False)
+        ]

As per coding guidelines: "Calendar and WebDAV writeback source selection must resolve through opaque source_uid values, signed-session organization scope, and persisted writeback eligibility, not sequential CalDAV or WebDAV account ids."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/services/webdav_service.py` at line 221, The list-comprehension
filtering WebDAV accounts currently treats a missing "writeback_enabled" key as
True; change that default to False so missing eligibility fails closed. Locate
the filter that iterates over accounts (the comprehension using
account.get("writeback_enabled", True)) and update it to
account.get("writeback_enabled", False); ensure any related logic that expects
persisted writeback eligibility still uses the "writeback_enabled" key and that
tests/mocks are adjusted if they relied on the old default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/services/webdav_service.py`:
- Line 221: The list-comprehension filtering WebDAV accounts currently treats a
missing "writeback_enabled" key as True; change that default to False so missing
eligibility fails closed. Locate the filter that iterates over accounts (the
comprehension using account.get("writeback_enabled", True)) and update it to
account.get("writeback_enabled", False); ensure any related logic that expects
persisted writeback eligibility still uses the "writeback_enabled" key and that
tests/mocks are adjusted if they relied on the old default.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c913a9ec-94b9-4a9a-8454-bf699b9186a7

📥 Commits

Reviewing files that changed from the base of the PR and between dc902f0 and c79fdd2.

📒 Files selected for processing (15)
  • AGENTS.md
  • README.md
  • backend/api/webdav.py
  • backend/db/models.py
  • backend/scripts/bootstrap_db.py
  • backend/services/webdav_service.py
  • backend/tests/test_bootstrap_db.py
  • backend/tests/test_webdav_api.py
  • docs/operations/source-of-truth-and-writeback-sovereignty.md
  • docs/plans/2026-05-27-data-webdav-writeback-intent-ui.md
  • docs/plans/2026-05-27-self-sent-webdav-materialization-intent.md
  • docs/plans/2026-05-27-webdav-opaque-source-id.md
  • frontend/src/app/data/page.test.tsx
  • frontend/src/components/DataLayout.tsx
  • frontend/tests/e2e/helpers.ts
✅ Files skipped from review due to trivial changes (5)
  • docs/operations/source-of-truth-and-writeback-sovereignty.md
  • frontend/src/app/data/page.test.tsx
  • AGENTS.md
  • docs/plans/2026-05-27-data-webdav-writeback-intent-ui.md
  • docs/plans/2026-05-27-self-sent-webdav-materialization-intent.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • docs/plans/2026-05-27-webdav-opaque-source-id.md
  • README.md
  • backend/tests/test_bootstrap_db.py
  • backend/scripts/bootstrap_db.py
  • backend/db/models.py
  • frontend/src/components/DataLayout.tsx
  • backend/api/webdav.py
  • frontend/tests/e2e/helpers.ts
  • backend/tests/test_webdav_api.py

@seonghobae

Copy link
Copy Markdown
Contributor Author

Updated Strix evidence for current head bb9258ec17ca70efb1c216bc4a8c3239f81c79fe after the fail-closed eligibility fix:

  • Application CI, Bandit security, CodeQL, PR Governance, Docker image validation, and CodeRabbit are green on the current head.
  • Strix again used direct OpenAI Platform only: provider_mode=openai_direct, STRIX_LLM_DEFAULT_PROVIDER: openai, and egress to api.openai.com.
  • The failure is OpenAI Platform quota exhaustion: RateLimitError / exceeded your current quota; no GitHub Models path or github.token LLM fallback was used.
  • Key log evidence is stored locally at /tmp/naruon-pr247-evidence/strix-current-head-bb9258e-key-lines.txt.

I will temporarily remove only the required strix context, merge this current-head PR through the normal merge path, then immediately restore security, CodeRabbit, and strix on master.

@seonghobae
seonghobae merged commit 07e3881 into master May 27, 2026
15 of 16 checks passed
@seonghobae
seonghobae deleted the feature/webdav-opaque-writeback-source-20260527 branch May 27, 2026 18:46
@seonghobae

Copy link
Copy Markdown
Contributor Author

Post-merge restore complete for PR #247: required contexts are back to security, CodeRabbit, and strix; open PR list is empty. Merge commit: 07e3881077372cc90b679feffddbdf89e1e1f134.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant