Skip to content

One-paste connect line from Chronicle, and report the module version - #121

Merged
keyxmakerx merged 2 commits into
mainfrom
claude/foundry-calendar-sync-ayr0rv
Oct 3, 2026
Merged

keyxmakerx merged 2 commits into
mainfrom
claude/foundry-calendar-sync-ayr0rv

Conversation

@keyxmakerx

Copy link
Copy Markdown
Owner

Requested by Key Maker · project thread

Fixes: none (module half of keyxmakerx/Chronicle#893)
Security implication: The pasted line carries an API key. It goes only into the existing CLIENT-scoped apiKey setting; the line itself sits in a client-scoped setting that is masked as a password field and cleared straight after applying. Only a GM can apply it, and the line and key are never logged.
Consumer-verified: Chronicle builds the line in internal/plugins/campaigns/foundry_connect.go BuildFoundryConnectLine (keyxmakerx/Chronicle#954). It reads the header in internal/plugins/syncapi/middleware.go (moduleVersionFromHeader, ^[0-9A-Za-z.+\-]{1,32}$) and allows it in CORS in internal/middleware/cors.go.
Foundry compatibility: n/a for live checks. Not run in a Foundry world here; the live check is keyxmakerx/Chronicle#966.
Mockup: Sign-offs card "Before and after: the Foundry row on Apps & game system", approved.

What this changes

Before: Connecting Foundry meant copying Chronicle's address, the campaign ID and an API key into three separate settings. Chronicle couldn't tell which module version was calling.

After:

  • A GM pastes the one line Chronicle shows (Manage › Apps & game system › Foundry VTT › Make a connect line) into the new Connect line setting. It fills in the address, campaign ID and key, then clears itself.
  • Every API request tells Chronicle the module version, so the owner sees it on that row.

Why

keyxmakerx/Chronicle#893. Old keys keep working (the owner's choice), so pasting a new line never disconnects anything else.

Load-bearing lines

  • scripts/_connect-line.mjs parseConnectLine:
    • chronicle:// maps to https and chronicle+http:// maps to http.
    • The path before the final /c/<id> is the base path; key is the query param.
    • It rejects userinfo, other schemes and missing parts.
  • scripts/settings.mjs applyConnectLine:
    • GM only, all-or-nothing, and always clears the line afterwards.
    • The running sync reads its settings only at start, so the GM is told to reload.
  • scripts/_module-version.mjs and api-client.mjs:
    • The header goes on fetch and uploadMedia, and caller headers win.
    • Compatibility with older servers: an older Chronicle's CORS allow-list doesn't name the header, so the browser refuses the preflight. A network TypeError on a request that carried the header retries once without it and drops it for the session, so installing this release before updating Chronicle doesn't break sync. The dashboard's raw probes never send it.
  • API-CONTRACT.md also records Chronicle's retired calendar routes (410 calendar_route_retired) and POST /calendar (201 {created, warnings}, 409 when a calendar exists), matching Chronicle#954.

Test plan

  • node --test tools/test-*.mjs: 967 tests, 967 pass, 0 fail.
  • node --check on every changed script.
  • New tests: tools/test-connect-line.mjs (6 accept and 12 reject cases, plus settings wiring) and tools/test-module-version-header.mjs (builder, live reader, the header-less retry and per-session drop, the fetch-path pins).
  • Manual check in Foundry: Chronicle#966, after Chronicle#954 is deployed and this module is released.
  • CI passes.

Tenet self-check

  • T-B1 security: the key stays client-scoped, is masked, is never logged, and only a GM can apply it.
  • T-B2 plugin isolation: the changes stay within the module.
  • T-B3 production UI: one settings field with success, warning and error toasts.
  • T-B4 docs: .ai.md, API-CONTRACT.md, README and CLAUDE.md describe it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UexL55BkZcztyC1eezXKfn


Generated by Claude Code

claude added 2 commits October 3, 2026 03:31
Every REST request carries X-Chronicle-Module-Version. A GM can paste
Chronicle's connect line into a client-scoped setting that fills URL,
campaign ID and the client-scoped API key, then clears itself. Contract
docs now mark /calendar/advance, /advance-time and /import retired (410)
and document POST /calendar.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UexL55BkZcztyC1eezXKfn
An older Chronicle's CORS allow-list lacks X-Chronicle-Module-Version, so a
cross-origin preflight carrying it is refused. The API client retries such
a request once without the header and stops sending it for the session;
the dashboard's raw probes no longer send it. The connect-line hint and
README now point at Manage > Apps & game system, where Chronicle shows it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UexL55BkZcztyC1eezXKfn
@keyxmakerx
keyxmakerx marked this pull request as ready for review October 3, 2026 03:52
@keyxmakerx keyxmakerx self-assigned this Oct 3, 2026
@keyxmakerx
keyxmakerx merged commit fa3f0b2 into main Oct 3, 2026
1 check passed
@keyxmakerx
keyxmakerx deleted the claude/foundry-calendar-sync-ayr0rv branch October 3, 2026 04:27
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.

2 participants