diff --git a/apps/pwa/src/lib/html.mjs b/apps/pwa/src/lib/html.mjs index 9813ff84..78a18d1c 100644 --- a/apps/pwa/src/lib/html.mjs +++ b/apps/pwa/src/lib/html.mjs @@ -123,6 +123,6 @@ export function appBar(user, balance, csrf = "") { export const footer = ``; diff --git a/apps/pwa/src/lib/legal.mjs b/apps/pwa/src/lib/legal.mjs new file mode 100644 index 00000000..3a1054fa --- /dev/null +++ b/apps/pwa/src/lib/legal.mjs @@ -0,0 +1,96 @@ +// Privacy Policy and Terms of Service for the moshcode product: moshcode.sh +// (the landing), app.moshcode.sh (this app, including the pit) and its MCP. +// The landing serves the same text (moshcode/moshcode.sh src/legal.mjs); keep +// the two copies in step. + +export const LEGAL_EFFECTIVE = "November 11, 2026"; +const CONTACT = "support@profullstack.com"; +const OPERATOR = "Profullstack, Inc., 16375 Bonnie Lane, Los Gatos, CA 95032, USA"; +const mail = `${CONTACT}`; + +export const PRIVACY_HTML = ` +

moshcode is operated by ${OPERATOR}. This policy covers moshcode.sh, app.moshcode.sh (including the pit and the Moshpit name registry), the moshcode MCP server, and what the moshcode CLI sends to them. It describes only what the service actually collects.

+ +

The CLI runs on your machine

+

The moshcode CLI installs and drives coding agents (opencode, Claude Code, codex and others) on your own computer. Your code, prompts and the agents' own accounts stay between you and those tools. The CLI sends something to app.moshcode.sh only when you use a feature that needs it: an approval, a live session mirror, the herd on your phone, settings sync, or the pit.

+ +

What we collect

+

Account. Your email address and a password (stored only as a hash), or a passkey (we keep its public key and a counter), or Sign in with CoinPay (we keep the CoinPay account identifier). An optional display name. A session cookie while you are signed in. API keys and CLI sign-ins are stored as hashes with a short prefix.

+

Approvals. When a script asks for one, we store its message, the context the script sent, the script name, and your reply.

+

Notification channels. The channels you add: an email address, a Slack incoming webhook URL, a Telegram chat id, browser push subscriptions, or a phone number for SMS.

+

Live sessions and the herd. If you mirror a CLI session or turn on the herd for your phone, the CLI sends the terminal output of that session (we keep the most recent part as scrollback), the session and machine names, and their status, and we queue the lines and keys you send back until the CLI collects them.

+

Teams. Organizations, teams and members you create, and the sessions you share with them.

+

Settings sync. The settings snapshots you save from the pit with /save; you can delete them from your settings.

+

Credits and payments. Credits are prepaid in cryptocurrency through CoinPay. We keep a ledger of credits bought and spent, purchase amounts and status, credit codes you redeem and invites you send. We never see card or bank details.

+

Moshpit names. Names and endings you register or buy, their records (addresses, sites, links, feeds and content you publish to them), and offers. If you opt in to a contact address for a name, mail to it is forwarded to you through Forward Email without publishing your own address.

+ +

Cookies and local storage

+

Signing in sets a session cookie, and forms carry a CSRF token. An invite or referral link sets a cookie so a later sign-up can be credited. There are no advertising cookies.

+ +

Who else is involved

+ +

Nothing is sold, and no advertising profile is built from you.

+ +

What is public

+

Registered Moshpit names, their records and anything you publish to them are public. Approvals, sessions, settings, channels and payments are visible only to your account and the teams you share them with.

+ +

AI agents and MCP

+

The moshcode MCP server is at https://app.moshcode.sh/mcp (JSON-RPC 2.0, streamable HTTP), and a single shared session is reachable at https://moshcode.sh/api/v1/mcp/<share-id>. There are no anonymous tools: every call needs an OAuth 2.1 access token (authorization code with PKCE, or device authorization) that you approve in the browser. With it, an MCP client can list your live moshcode sessions, read their output, and, only with the permissions you grant, send a line or a key, answer or approve a prompt, or cancel the session. Access tokens last at most an hour, and revoking the grant or the share stops them at once.

+

MCP and API requests are logged (time, tool or route, IP address, and the account or key if any) for abuse prevention and rate limiting. MCP actions are also recorded in an audit log of who did what and when, without the command text or terminal output. No MCP data is sold or used for advertising.

+ +

Keeping and deleting data

+

Session output is kept as a rolling scrollback, not a full history. Settings snapshots can be deleted from your settings. Everything else is kept until you delete it or ask us to. To delete your account, write to ${mail} from the address on the account.

+ +

Your rights

+

Depending on where you live, you may have the right to access, correct, delete or export your personal data. Contact us to exercise them.

+ +

Children

+

moshcode is a developer tool and is not directed at children under 13, and we do not knowingly collect personal information from them.

+ +

Changes

+

If the service starts doing something different, this page changes with it. Material changes will be announced on this page.

+ +

Contact

+

${OPERATOR}. Email ${mail}.

`; + +export const TERMS_HTML = ` +

1. Acceptance of terms

+

By accessing or using moshcode.sh, app.moshcode.sh, the moshcode CLI's online features or the moshcode MCP server (the "Service"), you agree to these Terms of Service. If you do not agree, do not use the Service. The Service is operated by ${OPERATOR}.

+ +

2. Use of the Service

+

moshcode drives coding agents, routes approvals to you, mirrors your sessions and runs the Moshpit name registry. You are responsible for what your agents and scripts do with it. Do not use the Service to attack, spam or harm others, to publish illegal content on a Moshpit name, or to get around access controls. Please do not send automated traffic heavy enough to degrade the Service for anyone else; on moshcode.sh, training crawlers and scrapers must buy a day pass.

+ +

3. Accounts

+

You are responsible for your account, API keys, OAuth grants and session shares, and all activity under them. Tell us at once about any unauthorized use.

+ +

4. Payments

+

Credits, Moshpit names and crawler day passes are paid in cryptocurrency through CoinPay, or over x402. Credits pay for metered notification channels as they are used.

+ +

5. Your content

+

You keep whatever rights you have in what you send, share or publish, and you confirm you may share it. You give us permission to store, process and show it as needed to run the Service.

+ +

6. MCP and API use

+

The MCP server at https://app.moshcode.sh/mcp, shared session endpoints and the API are part of the Service and fall under these terms, including the acceptable use rules above. An agent acting with a grant you approved acts on your behalf, and you are responsible for what it does. A grant with write access can type into your terminal session; grant only what you mean to.

+ +

7. Intellectual property

+

All content, code and trademarks associated with moshcode are the property of Profullstack, Inc. or its licensors. moshcode's source code is available under the MIT License, and open-source components are governed by their respective licenses.

+ +

8. Disclaimer of warranties

+

The Service is provided "as is" without warranties of any kind. We do not guarantee that it will be uninterrupted, secure or error-free.

+ +

9. Limitation of liability

+

To the maximum extent permitted by law, Profullstack, Inc. shall not be liable for any indirect, incidental, special, consequential or punitive damages arising out of your use of the Service.

+ +

10. Changes to terms

+

We may update these terms from time to time. Continued use of the Service after a change means you accept the revised terms.

+ +

11. Contact

+

Questions about these terms? Email ${mail}.

`; diff --git a/apps/pwa/src/routes/legal.mjs b/apps/pwa/src/routes/legal.mjs new file mode 100644 index 00000000..5662cc10 --- /dev/null +++ b/apps/pwa/src/routes/legal.mjs @@ -0,0 +1,37 @@ +// /privacy and /terms: server-rendered, public, in the brand shell. +import { Router } from "express"; +import { page, footer } from "../lib/html.mjs"; +import { ringFooter } from "../lib/ring-footer.mjs"; +import { LEGAL_EFFECTIVE, PRIVACY_HTML, TERMS_HTML } from "../lib/legal.mjs"; + +export const legalRouter = Router(); + +const LEGAL_CSS = ``; + +function legalPage(title, other, html) { + const body = ` +
+ MMOSHCODEapp +

${title}

+

Effective date: ${LEGAL_EFFECTIVE} · ${other}

+ ${html} +
${footer} +
${ringFooter()}
`; + return page({ title: `${title} · moshcode`, head: LEGAL_CSS, body }); +} + +legalRouter.get(["/privacy", "/privacy/"], (_req, res) => { + res.type("html").send(legalPage("Privacy Policy", `Terms of Service`, PRIVACY_HTML)); +}); + +legalRouter.get(["/terms", "/terms/"], (_req, res) => { + res.type("html").send(legalPage("Terms of Service", `Privacy Policy`, TERMS_HTML)); +}); diff --git a/apps/pwa/src/server.mjs b/apps/pwa/src/server.mjs index c7267d67..6f63a4d1 100644 --- a/apps/pwa/src/server.mjs +++ b/apps/pwa/src/server.mjs @@ -17,6 +17,7 @@ import { organizationsRouter } from "./routes/organizations.mjs"; import { mcpRouter } from "./routes/mcp.mjs"; import { mcpOAuthBrowserRouter, mcpOAuthMachineRouter } from "./routes/mcp-oauth.mjs"; import { pagesRouter } from "./routes/pages.mjs"; +import { legalRouter } from "./routes/legal.mjs"; import { settingsSyncRouter } from "./routes/settings-sync.mjs"; import { moshpitRouter } from "./routes/moshpit.mjs"; import { socialsRouter } from "./routes/socials.mjs"; @@ -99,6 +100,7 @@ app.use(mcpRouter); app.use(csrfGuard); // routes +app.use(legalRouter); // /privacy, /terms app.use(authRouter); // GET / (+ /auth/login|register|logout) app.use(passkeyRouter); app.use(coinpayRouter); diff --git a/apps/pwa/test/legal-pages.test.mjs b/apps/pwa/test/legal-pages.test.mjs new file mode 100644 index 00000000..47d8df71 --- /dev/null +++ b/apps/pwa/test/legal-pages.test.mjs @@ -0,0 +1,36 @@ +// /privacy and /terms answer 200 with their headings, outside the session and +// database, and the brand footer links them. +import assert from "node:assert/strict"; +import { createRequire } from "node:module"; +import test from "node:test"; + +const require = createRequire(import.meta.url); +let express = null; +try { express = require("express"); } catch { express = null; } + +test("legal pages render, link each other, and carry no dashes", { skip: !express && "pwa deps not installed" }, async () => { + const { legalRouter } = await import("../src/routes/legal.mjs"); + const { footer } = await import("../src/lib/html.mjs"); + assert.match(footer, /href="\/privacy"/); + assert.match(footer, /href="\/terms"/); + const app = express(); + app.use(legalRouter); + const server = app.listen(0); + await new Promise((resolve) => server.once("listening", resolve)); + const base = `http://127.0.0.1:${server.address().port}`; + try { + for (const [path, heading] of [["/privacy", "Privacy Policy"], ["/terms", "Terms of Service"]]) { + const res = await fetch(base + path); + assert.equal(res.status, 200); + const html = await res.text(); + assert.ok(html.includes(`

${heading}

`)); + assert.ok(html.includes(`${heading}`)); + assert.ok(html.includes("Effective date: November 11, 2026")); + assert.ok(html.includes('href="/privacy"') && html.includes('href="/terms"')); + assert.doesNotMatch(html, /[–—]/); + } + assert.match(await (await fetch(base + "/privacy")).text(), /app\.moshcode\.sh\/mcp/); + } finally { + await new Promise((resolve) => server.close(resolve)); + } +});