Skip to content

Commit 557fb99

Browse files
plcclaude
andcommitted
Add Terms of Service and Privacy Policy pages, add footer to all pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent da4f2f5 commit 557fb99

File tree

5 files changed

+184
-1
lines changed

5 files changed

+184
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1717

1818
- **Postmark webhook event logging** — new endpoint ingests Postmark delivery, bounce, spam, open, and click events into a `postmark_webhooks` table for email deliverability debugging. GET the same URL to view recent events.
1919
- **Welcome event on new calendars** — new calendars automatically get a "Send Peter feedback" event at 9am the next day (in the calendar's timezone), with an invite sent to peter.clark@gmail.com.
20+
- **Terms of Service and Privacy Policy** — new `/terms` and `/privacy` pages with footer links on landing, docs, and quickstart pages.
2021

2122
### Fixed
2223
- **`trust proxy` for Fly.io** — set `app.set('trust proxy', 1)` so `express-rate-limit` correctly identifies clients behind Fly's reverse proxy. Fixes `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` validation errors on every cold start.

src/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const quickstartRouter = require('./routes/quickstart');
3131
const manRouter = require('./routes/man');
3232
const viewRouter = require('./routes/view');
3333
const postmarkWebhooksRouter = require('./routes/postmark-webhooks');
34+
const legalRouter = require('./routes/legal');
3435
const { extendAllHorizons, EXTEND_INTERVAL_MS } = require('./lib/recurrence');
3536

3637
const app = express();
@@ -150,16 +151,23 @@ curl -s -X POST https://${DOMAIN}/man?guide</code></pre>
150151
<div class="links">
151152
<a href="/docs" class="primary">API Docs</a>
152153
</div>
154+
155+
<footer style="margin-top:3rem; padding-top:1.5rem; border-top:1px solid #334155; text-align:center; font-size:0.8125rem; color:#64748b;">
156+
<a href="/terms" style="color:#94a3b8; text-decoration:none;">Terms</a> &middot;
157+
<a href="/privacy" style="color:#94a3b8; text-decoration:none;">Privacy</a> &middot;
158+
Created by <a href="https://plc.vc/qbs" style="color:#94a3b8; text-decoration:none;">Peter Clark</a>
159+
</footer>
153160
</div>
154161
</body>
155162
</html>`;
156163

157164
res.send(html);
158165
});
159166

160-
// API documentation and quick start (no auth)
167+
// API documentation, quick start, and legal pages (no auth)
161168
app.use('/docs', docsRouter);
162169
app.use('/quickstart', quickstartRouter);
170+
app.use('/', legalRouter);
163171
// Machine-readable API manual (optional auth handled internally)
164172
app.use('/man', manRouter);
165173
// Agent provisioning (no auth, strict rate limit)

src/routes/docs.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@ Daily standup 2026-02-13 16:00:00Z ...
489489
}'</code></pre>
490490
</div>
491491
492+
<footer style="margin-top:3rem; padding-top:1.5rem; border-top:1px solid #334155; text-align:center; font-size:0.8125rem; color:#64748b;">
493+
<a href="/terms" style="color:#94a3b8; text-decoration:none;">Terms</a> &middot;
494+
<a href="/privacy" style="color:#94a3b8; text-decoration:none;">Privacy</a> &middot;
495+
Created by <a href="https://plc.vc/qbs" style="color:#94a3b8; text-decoration:none;">Peter Clark</a>
496+
</footer>
492497
</div>
493498
494499
<script>

src/routes/legal.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/**
2+
* Legal pages — Terms of Service and Privacy Policy
3+
*
4+
* GET /terms — Terms of Service
5+
* GET /privacy — Privacy Policy
6+
*/
7+
8+
const { Router } = require('express');
9+
const router = Router();
10+
11+
const DOMAIN = process.env.CALDAVE_DOMAIN || 'caldave.ai';
12+
13+
const HEAD = `<!DOCTYPE html>
14+
<html lang="en">
15+
<head>
16+
<meta charset="UTF-8">
17+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
18+
<style>
19+
* { box-sizing: border-box; margin: 0; padding: 0; }
20+
body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; padding: 2rem; }
21+
.container { max-width: 800px; margin: 0 auto; }
22+
h1 { font-size: 2rem; color: #fff; margin-bottom: 0.5rem; }
23+
h1 a { color: #94a3b8; text-decoration: none; font-size: 0.875rem; }
24+
h1 a:hover { color: #e2e8f0; }
25+
h2 { font-size: 1.25rem; color: #fff; margin-top: 2rem; margin-bottom: 0.75rem; }
26+
p, li { font-size: 0.9375rem; color: #cbd5e1; line-height: 1.7; margin-bottom: 0.75rem; }
27+
ul { padding-left: 1.5rem; margin-bottom: 1rem; }
28+
a { color: #60a5fa; }
29+
.updated { font-size: 0.8125rem; color: #64748b; margin-bottom: 2rem; }
30+
footer { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid #334155; text-align: center; font-size: 0.8125rem; color: #64748b; }
31+
footer a { color: #94a3b8; text-decoration: none; }
32+
footer a:hover { color: #e2e8f0; }
33+
</style>`;
34+
35+
const FOOTER = ` <footer>
36+
<a href="/terms">Terms</a> &middot;
37+
<a href="/privacy">Privacy</a> &middot;
38+
Created by <a href="https://plc.vc/qbs">Peter Clark</a>
39+
</footer>`;
40+
41+
router.get('/terms', (req, res) => {
42+
res.send(`${HEAD}
43+
<title>Terms of Service - CalDave</title>
44+
</head>
45+
<body>
46+
<div class="container">
47+
<h1><a href="/">&larr; Home</a></h1>
48+
<h1>Terms of Service</h1>
49+
<p class="updated">Last updated: February 2026</p>
50+
51+
<h2>1. Acceptance of Terms</h2>
52+
<p>By accessing or using the CalDave API ("Service"), you agree to be bound by these Terms of Service. If you do not agree, do not use the Service.</p>
53+
54+
<h2>2. Description of Service</h2>
55+
<p>CalDave provides a calendar-as-a-service REST API designed for AI agents. The Service allows you to create calendars, manage events, send and receive calendar invites via email, and subscribe to iCal feeds.</p>
56+
57+
<h2>3. API Access</h2>
58+
<p>You are responsible for keeping your API keys secure. Do not share your API keys publicly or embed them in client-side code. You are responsible for all activity that occurs under your API key.</p>
59+
60+
<h2>4. Acceptable Use</h2>
61+
<p>You agree not to:</p>
62+
<ul>
63+
<li>Use the Service to send spam, unsolicited emails, or abuse the email invite functionality</li>
64+
<li>Attempt to disrupt or overload the Service infrastructure</li>
65+
<li>Use the Service for any unlawful purpose</li>
66+
<li>Reverse engineer or attempt to extract the source code of the Service</li>
67+
<li>Resell access to the Service without permission</li>
68+
</ul>
69+
70+
<h2>5. Rate Limits</h2>
71+
<p>The Service enforces rate limits to ensure fair usage. Exceeding rate limits may result in temporary or permanent restriction of access.</p>
72+
73+
<h2>6. Data Ownership</h2>
74+
<p>You retain ownership of all data you create through the Service, including events, calendar names, and metadata. CalDave does not claim ownership of your data.</p>
75+
76+
<h2>7. Service Availability</h2>
77+
<p>CalDave is provided on an "as is" and "as available" basis. We do not guarantee uninterrupted or error-free operation. We may modify, suspend, or discontinue the Service at any time with reasonable notice.</p>
78+
79+
<h2>8. Limitation of Liability</h2>
80+
<p>To the maximum extent permitted by law, CalDave shall not be liable for any indirect, incidental, special, consequential, or punitive damages, or any loss of data, profits, or revenue arising from your use of the Service.</p>
81+
82+
<h2>9. Account Termination</h2>
83+
<p>We reserve the right to suspend or terminate your access to the Service at any time for violation of these terms or for any reason with reasonable notice. You may stop using the Service at any time.</p>
84+
85+
<h2>10. Changes to Terms</h2>
86+
<p>We may update these terms from time to time. Continued use of the Service after changes constitutes acceptance of the updated terms.</p>
87+
88+
<h2>11. Contact</h2>
89+
<p>Questions about these terms? Email <a href="mailto:peterclark@me.com">peterclark@me.com</a>.</p>
90+
91+
${FOOTER}
92+
</div>
93+
</body>
94+
</html>`);
95+
});
96+
97+
router.get('/privacy', (req, res) => {
98+
res.send(`${HEAD}
99+
<title>Privacy Policy - CalDave</title>
100+
</head>
101+
<body>
102+
<div class="container">
103+
<h1><a href="/">&larr; Home</a></h1>
104+
<h1>Privacy Policy</h1>
105+
<p class="updated">Last updated: February 2026</p>
106+
107+
<h2>1. What We Collect</h2>
108+
<p>When you use CalDave, we collect and store:</p>
109+
<ul>
110+
<li><strong>Agent credentials</strong> — agent IDs and hashed API keys (we never store your raw API key after initial creation)</li>
111+
<li><strong>Calendar data</strong> — calendar names, timezones, and generated email addresses</li>
112+
<li><strong>Event data</strong> — event titles, descriptions, times, locations, attendees, and metadata you provide</li>
113+
<li><strong>Inbound emails</strong> — calendar invite emails sent to your calendar's email address, including sender information and .ics attachments</li>
114+
<li><strong>Email delivery logs</strong> — when outbound emails are sent (invites, RSVP replies), we log delivery status for debugging</li>
115+
</ul>
116+
117+
<h2>2. How We Use Your Data</h2>
118+
<p>Your data is used exclusively to provide the CalDave service:</p>
119+
<ul>
120+
<li>Storing and serving your calendar events via the API</li>
121+
<li>Generating iCal feeds for calendar subscriptions</li>
122+
<li>Processing inbound email invites</li>
123+
<li>Sending outbound calendar invite and RSVP emails on your behalf</li>
124+
</ul>
125+
126+
<h2>3. What We Do Not Do</h2>
127+
<ul>
128+
<li>We do not sell your data to third parties</li>
129+
<li>We do not use your data for advertising</li>
130+
<li>We do not train AI models on your data</li>
131+
<li>We do not share your data with third parties except as needed to operate the Service (e.g., Postmark for email delivery)</li>
132+
</ul>
133+
134+
<h2>4. Third-Party Services</h2>
135+
<p>CalDave uses the following third-party services:</p>
136+
<ul>
137+
<li><strong>Fly.io</strong> — application hosting</li>
138+
<li><strong>Postmark</strong> — inbound and outbound email processing</li>
139+
</ul>
140+
<p>These providers have their own privacy policies governing their handling of data.</p>
141+
142+
<h2>5. Data Storage and Security</h2>
143+
<p>Your data is stored in a PostgreSQL database hosted on Fly.io infrastructure. API keys are hashed using SHA-256 before storage. All connections to the API are encrypted via HTTPS.</p>
144+
145+
<h2>6. Data Retention</h2>
146+
<p>Your data is retained as long as your agent account exists. If you delete a calendar, all associated events are permanently deleted. There is no soft-delete or recycle bin.</p>
147+
148+
<h2>7. Data Export</h2>
149+
<p>You can export your data at any time through the API (list events) or by subscribing to your calendar's iCal feed.</p>
150+
151+
<h2>8. Changes to This Policy</h2>
152+
<p>We may update this policy from time to time. Continued use of the Service after changes constitutes acceptance of the updated policy.</p>
153+
154+
<h2>9. Contact</h2>
155+
<p>Questions about privacy? Email <a href="mailto:peterclark@me.com">peterclark@me.com</a>.</p>
156+
157+
${FOOTER}
158+
</div>
159+
</body>
160+
</html>`);
161+
});
162+
163+
module.exports = router;

src/routes/quickstart.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ router.get('/', (req, res) => {
163163
<p>Works with Google Calendar, Outlook, Apple Calendar, or any app that sends .ics invites.</p>
164164
</div>
165165
</div>
166+
167+
<footer style="margin-top:3rem; padding-top:1.5rem; border-top:1px solid #334155; text-align:center; font-size:0.8125rem; color:#64748b;">
168+
<a href="/terms" style="color:#94a3b8; text-decoration:none;">Terms</a> &middot;
169+
<a href="/privacy" style="color:#94a3b8; text-decoration:none;">Privacy</a> &middot;
170+
Created by <a href="https://plc.vc/qbs" style="color:#94a3b8; text-decoration:none;">Peter Clark</a>
171+
</footer>
166172
</div>
167173
168174
<script>

0 commit comments

Comments
 (0)