@orangecheck/legal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OrangeCheck (OCHK)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @orangecheck/legal
2
+
3
+ Family-internal legal-document engine for the `.ochk.io` sub-sites. **Not for
4
+ third-party integration.**
5
+
6
+ It composes **Terms of Service** and **Privacy Policy** pages from one shared
7
+ clause library plus per-profile content, so the whole family stays in sync
8
+ from a single source of truth — while the commercial products keep the
9
+ variants they genuinely need.
10
+
11
+ ## Why this exists
12
+
13
+ The ecosystem outgrew "one Terms page on ochk.io". The non-custodial protocol
14
+ sites, a consumer product that pays users in sats, a paid encrypted vault, and
15
+ an enterprise B2B product cannot share one legal document. This package keeps
16
+ the boilerplate shared and the divergence explicit.
17
+
18
+ ## Profiles
19
+
20
+ | Profile | Sites |
21
+ |------------|-------------------------------------------------------------------|
22
+ | `protocol` | ochk.io + the six verb sites + docs + analytics (one shared doc) |
23
+ | `me` | me.ochk.io — consumer identity that pays users in sats |
24
+ | `vault` | vault.ochk.io — paid end-to-end-encrypted secrets vault |
25
+ | `fleet` | fleet.ochk.io — enterprise managed agent infrastructure |
26
+
27
+ The nine `protocol` sites do **not** host their own pages — their footers link
28
+ to ochk.io. Only the three commercial products self-host.
29
+
30
+ ## Usage
31
+
32
+ ```tsx
33
+ // src/pages/terms.tsx on a self-hosting site
34
+ import { buildDoc, LegalDocument } from '@orangecheck/legal';
35
+ import { Seo } from '@/components/layout/Seo';
36
+
37
+ const doc = buildDoc('me', 'terms'); // pure + synchronous
38
+
39
+ export default function TermsPage() {
40
+ return (
41
+ <>
42
+ <Seo title={doc.metaTitle} description={doc.metaDescription} canonical="/terms" />
43
+ <div className="container py-12">
44
+ <LegalDocument doc={doc} />
45
+ </div>
46
+ </>
47
+ );
48
+ }
49
+ ```
50
+
51
+ Tailwind 4 must scan the package so its utility classes emit. In `globals.css`:
52
+
53
+ ```css
54
+ @source '../../node_modules/@orangecheck/legal';
55
+ ```
56
+
57
+ Footer links resolve through one helper:
58
+
59
+ ```ts
60
+ import { legalHref } from '@orangecheck/legal';
61
+ legalHref('me', 'terms'); // → '/terms'
62
+ legalHref('stamp', 'terms'); // → 'https://ochk.io/terms'
63
+ ```
64
+
65
+ Security pages stay bespoke per product but share the disclosure block:
66
+
67
+ ```tsx
68
+ import { SecurityDisclosure } from '@orangecheck/legal';
69
+ <SecurityDisclosure securityContact="security@ochk.io" />
70
+ ```
71
+
72
+ ## Authoring
73
+
74
+ - Shared clauses live in `src/content/clauses.ts`.
75
+ - Each profile composes them in `src/content/<profile>.ts`.
76
+ - Strings use `[[TOKEN]]` placeholders (`ENTITY`, `PRODUCT`, `HOST`, `CONTACT`,
77
+ `SECURITY_CONTACT`, `GOVERNING_LAW`, `ARBITRATION_SEAT`) resolved by
78
+ `buildDoc`.
79
+ - A `stub` block renders a visible **"pending · counsel review"** notice — used
80
+ for money, custody, refund, SLA, and regulated-activity sections that must be
81
+ finalized by counsel before the commercial products reach general
82
+ availability.
83
+
84
+ ### Entity of record
85
+
86
+ No formal legal entity is registered yet. `LEGAL_ENTITY` in `src/constants.ts`
87
+ is the single swap point — set it (and `LEGAL_ENTITY_LONG`) when the entity is
88
+ formed, rebuild, and publish; every document family-wide updates.
89
+
90
+ ## Build
91
+
92
+ ```
93
+ npm install
94
+ npm run build
95
+ ```
@@ -0,0 +1,86 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type LegalProfile = 'protocol' | 'me' | 'vault' | 'fleet';
4
+ type DocKind = 'terms' | 'privacy';
5
+ type BulletItem = string | {
6
+ k: string;
7
+ v: string;
8
+ };
9
+ type Block = {
10
+ kind: 'para';
11
+ text: string;
12
+ } | {
13
+ kind: 'subhead';
14
+ text: string;
15
+ } | {
16
+ kind: 'bullets';
17
+ items: BulletItem[];
18
+ } | {
19
+ kind: 'callout';
20
+ text: string;
21
+ emphatic?: boolean;
22
+ } | {
23
+ kind: 'stub';
24
+ text: string;
25
+ };
26
+ interface Section {
27
+ id: string;
28
+ num?: string;
29
+ heading: string;
30
+ hint?: string;
31
+ blocks: Block[];
32
+ }
33
+ interface DocSpec {
34
+ kind: DocKind;
35
+ eyebrow: string;
36
+ title: string;
37
+ description: string;
38
+ metaTitle: string;
39
+ metaDescription: string;
40
+ effective: string;
41
+ updated: string;
42
+ preamble?: Block[];
43
+ sections: Section[];
44
+ summary?: string;
45
+ }
46
+ interface LegalDoc extends DocSpec {
47
+ site: string;
48
+ profile: LegalProfile;
49
+ }
50
+ interface SiteContext {
51
+ slug: string;
52
+ profile: LegalProfile;
53
+ host: string;
54
+ product: string;
55
+ productShort: string;
56
+ contact: string;
57
+ securityContact: string;
58
+ selfHosted: boolean;
59
+ }
60
+
61
+ declare function buildDoc(siteSlug: string, kind: DocKind): LegalDoc;
62
+
63
+ interface LegalDocumentProps {
64
+ doc: LegalDoc;
65
+ }
66
+ declare function LegalDocument({ doc }: LegalDocumentProps): react_jsx_runtime.JSX.Element;
67
+
68
+ interface SecurityDisclosureProps {
69
+ securityContact?: string;
70
+ heading?: string | null;
71
+ }
72
+ declare function SecurityDisclosure({ securityContact, heading, }: SecurityDisclosureProps): react_jsx_runtime.JSX.Element;
73
+
74
+ declare const LEGAL_SITES: Record<string, SiteContext>;
75
+ declare const PROTOCOL_LINKED_SITES: readonly ["attest", "lock", "vote", "stamp", "agent", "pledge", "docs", "analytics"];
76
+ declare function getSiteContext(slug: string): SiteContext;
77
+ declare function legalHref(siteSlug: string, kind: 'terms' | 'privacy' | 'security'): string;
78
+
79
+ declare const LEGAL_ENTITY = "OrangeCheck";
80
+ declare const LEGAL_ENTITY_LONG = "OrangeCheck (OCHK), an unincorporated project";
81
+ declare const ENTITY_PENDING = true;
82
+ declare const CONTACT_GENERAL = "hello@ochk.io";
83
+ declare const CONTACT_SECURITY = "security@ochk.io";
84
+ declare const CONTACT_PRIVACY = "hello@ochk.io";
85
+
86
+ export { type Block, type BulletItem, CONTACT_GENERAL, CONTACT_PRIVACY, CONTACT_SECURITY, type DocKind, type DocSpec, ENTITY_PENDING, LEGAL_ENTITY, LEGAL_ENTITY_LONG, LEGAL_SITES, type LegalDoc, LegalDocument, type LegalDocumentProps, type LegalProfile, PROTOCOL_LINKED_SITES, type Section, SecurityDisclosure, type SecurityDisclosureProps, type SiteContext, buildDoc, getSiteContext, legalHref };
@@ -0,0 +1,86 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type LegalProfile = 'protocol' | 'me' | 'vault' | 'fleet';
4
+ type DocKind = 'terms' | 'privacy';
5
+ type BulletItem = string | {
6
+ k: string;
7
+ v: string;
8
+ };
9
+ type Block = {
10
+ kind: 'para';
11
+ text: string;
12
+ } | {
13
+ kind: 'subhead';
14
+ text: string;
15
+ } | {
16
+ kind: 'bullets';
17
+ items: BulletItem[];
18
+ } | {
19
+ kind: 'callout';
20
+ text: string;
21
+ emphatic?: boolean;
22
+ } | {
23
+ kind: 'stub';
24
+ text: string;
25
+ };
26
+ interface Section {
27
+ id: string;
28
+ num?: string;
29
+ heading: string;
30
+ hint?: string;
31
+ blocks: Block[];
32
+ }
33
+ interface DocSpec {
34
+ kind: DocKind;
35
+ eyebrow: string;
36
+ title: string;
37
+ description: string;
38
+ metaTitle: string;
39
+ metaDescription: string;
40
+ effective: string;
41
+ updated: string;
42
+ preamble?: Block[];
43
+ sections: Section[];
44
+ summary?: string;
45
+ }
46
+ interface LegalDoc extends DocSpec {
47
+ site: string;
48
+ profile: LegalProfile;
49
+ }
50
+ interface SiteContext {
51
+ slug: string;
52
+ profile: LegalProfile;
53
+ host: string;
54
+ product: string;
55
+ productShort: string;
56
+ contact: string;
57
+ securityContact: string;
58
+ selfHosted: boolean;
59
+ }
60
+
61
+ declare function buildDoc(siteSlug: string, kind: DocKind): LegalDoc;
62
+
63
+ interface LegalDocumentProps {
64
+ doc: LegalDoc;
65
+ }
66
+ declare function LegalDocument({ doc }: LegalDocumentProps): react_jsx_runtime.JSX.Element;
67
+
68
+ interface SecurityDisclosureProps {
69
+ securityContact?: string;
70
+ heading?: string | null;
71
+ }
72
+ declare function SecurityDisclosure({ securityContact, heading, }: SecurityDisclosureProps): react_jsx_runtime.JSX.Element;
73
+
74
+ declare const LEGAL_SITES: Record<string, SiteContext>;
75
+ declare const PROTOCOL_LINKED_SITES: readonly ["attest", "lock", "vote", "stamp", "agent", "pledge", "docs", "analytics"];
76
+ declare function getSiteContext(slug: string): SiteContext;
77
+ declare function legalHref(siteSlug: string, kind: 'terms' | 'privacy' | 'security'): string;
78
+
79
+ declare const LEGAL_ENTITY = "OrangeCheck";
80
+ declare const LEGAL_ENTITY_LONG = "OrangeCheck (OCHK), an unincorporated project";
81
+ declare const ENTITY_PENDING = true;
82
+ declare const CONTACT_GENERAL = "hello@ochk.io";
83
+ declare const CONTACT_SECURITY = "security@ochk.io";
84
+ declare const CONTACT_PRIVACY = "hello@ochk.io";
85
+
86
+ export { type Block, type BulletItem, CONTACT_GENERAL, CONTACT_PRIVACY, CONTACT_SECURITY, type DocKind, type DocSpec, ENTITY_PENDING, LEGAL_ENTITY, LEGAL_ENTITY_LONG, LEGAL_SITES, type LegalDoc, LegalDocument, type LegalDocumentProps, type LegalProfile, PROTOCOL_LINKED_SITES, type Section, SecurityDisclosure, type SecurityDisclosureProps, type SiteContext, buildDoc, getSiteContext, legalHref };