@percher/shared 0.1.9 → 1.0.2
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 +132 -0
- package/dist/brand.d.ts +64 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +67 -0
- package/dist/brand.js.map +1 -0
- package/dist/client-ip-attestation.d.ts +67 -0
- package/dist/client-ip-attestation.d.ts.map +1 -0
- package/dist/client-ip-attestation.js +74 -0
- package/dist/client-ip-attestation.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/legal-changes.d.ts +118 -0
- package/dist/legal-changes.d.ts.map +1 -0
- package/dist/legal-changes.js +135 -0
- package/dist/legal-changes.js.map +1 -0
- package/dist/legal.d.ts +52 -9
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +47 -8
- package/dist/legal.js.map +1 -1
- package/dist/limits.d.ts +8 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +8 -0
- package/dist/limits.js.map +1 -1
- package/dist/oauth-client-name.d.ts +22 -0
- package/dist/oauth-client-name.d.ts.map +1 -0
- package/dist/oauth-client-name.js +37 -0
- package/dist/oauth-client-name.js.map +1 -0
- package/dist/oauth-scopes.d.ts +19 -0
- package/dist/oauth-scopes.d.ts.map +1 -0
- package/dist/oauth-scopes.js +31 -0
- package/dist/oauth-scopes.js.map +1 -0
- package/dist/platform-env.d.ts.map +1 -1
- package/dist/platform-env.js +6 -0
- package/dist/platform-env.js.map +1 -1
- package/dist/platform.d.ts +3 -3
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +3 -3
- package/dist/platform.js.map +1 -1
- package/dist/signup-invite-hash.d.ts +8 -0
- package/dist/signup-invite-hash.d.ts.map +1 -0
- package/dist/signup-invite-hash.js +11 -0
- package/dist/signup-invite-hash.js.map +1 -0
- package/dist/signup-invite.d.ts +23 -0
- package/dist/signup-invite.d.ts.map +1 -0
- package/dist/signup-invite.js +34 -0
- package/dist/signup-invite.js.map +1 -0
- package/dist/timeouts.d.ts +85 -0
- package/dist/timeouts.d.ts.map +1 -1
- package/dist/timeouts.js +84 -0
- package/dist/timeouts.js.map +1 -1
- package/package.json +12 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Scope and exceptions
|
|
2
|
+
|
|
3
|
+
This licence applies to Percher-authored source, tests, scripts, configuration
|
|
4
|
+
and generated artefacts in this package, whether published or unpublished.
|
|
5
|
+
Versions below 1.0.0 retain their original MIT grants; version 1.0.0 and later
|
|
6
|
+
use FSL-1.1-MIT. Earlier MIT permissions are not revoked or withdrawn.
|
|
7
|
+
|
|
8
|
+
Scaffold payloads from packages/core/templates and AI instruction payloads
|
|
9
|
+
from packages/core/src/templates/ai-files and docs/CLAUDE.md, including their distributed copies
|
|
10
|
+
and generated output, are licensed under 0BSD, as carried with those payloads.
|
|
11
|
+
This exception does not change the licence of a customer's existing project.
|
|
12
|
+
|
|
13
|
+
Third-party components retain their own licences, including components named
|
|
14
|
+
in THIRD-PARTY-NOTICES.txt and the Apache-2.0 fixture files enumerated in
|
|
15
|
+
packages/core/test/fixtures/supabase-apps/*/SOURCES.md under their own LICENSE.
|
|
16
|
+
FSL does not relicense those components. packages/mark is private and excluded
|
|
17
|
+
from all MIT, 0BSD and FSL grants here.
|
|
18
|
+
|
|
19
|
+
In case of overlap, third-party component licences take precedence, followed
|
|
20
|
+
by packages/mark/LICENSE, the 0BSD payload exception, this package LICENSE,
|
|
21
|
+
and finally the repository root LICENSE.
|
|
22
|
+
|
|
23
|
+
# Functional Source License, Version 1.1, MIT Future License
|
|
24
|
+
|
|
25
|
+
## Abbreviation
|
|
26
|
+
|
|
27
|
+
FSL-1.1-MIT
|
|
28
|
+
|
|
29
|
+
## Notice
|
|
30
|
+
|
|
31
|
+
Copyright 2026 Mattias Malmborg
|
|
32
|
+
|
|
33
|
+
## Terms and Conditions
|
|
34
|
+
|
|
35
|
+
### Licensor ("We")
|
|
36
|
+
|
|
37
|
+
The party offering the Software under these Terms and Conditions.
|
|
38
|
+
|
|
39
|
+
### The Software
|
|
40
|
+
|
|
41
|
+
The "Software" is each version of the software that we make available under
|
|
42
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
43
|
+
Conditions with the Software.
|
|
44
|
+
|
|
45
|
+
### License Grant
|
|
46
|
+
|
|
47
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
48
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
49
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
50
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
51
|
+
|
|
52
|
+
### Permitted Purpose
|
|
53
|
+
|
|
54
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
55
|
+
means making the Software available to others in a commercial product or
|
|
56
|
+
service that:
|
|
57
|
+
|
|
58
|
+
1. substitutes for the Software;
|
|
59
|
+
|
|
60
|
+
2. substitutes for any other product or service we offer using the Software
|
|
61
|
+
that exists as of the date we make the Software available; or
|
|
62
|
+
|
|
63
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
64
|
+
|
|
65
|
+
Permitted Purposes specifically include using the Software:
|
|
66
|
+
|
|
67
|
+
1. for your internal use and access;
|
|
68
|
+
|
|
69
|
+
2. for non-commercial education;
|
|
70
|
+
|
|
71
|
+
3. for non-commercial research; and
|
|
72
|
+
|
|
73
|
+
4. in connection with professional services that you provide to a licensee
|
|
74
|
+
using the Software in accordance with these Terms and Conditions.
|
|
75
|
+
|
|
76
|
+
### Patents
|
|
77
|
+
|
|
78
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
79
|
+
patents, the license grant above includes a license under our patents. If you
|
|
80
|
+
make a claim against any party that the Software infringes or contributes to
|
|
81
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
82
|
+
immediately.
|
|
83
|
+
|
|
84
|
+
### Redistribution
|
|
85
|
+
|
|
86
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
87
|
+
the Software.
|
|
88
|
+
|
|
89
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
90
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
91
|
+
remove any copyright notices provided in or with the Software.
|
|
92
|
+
|
|
93
|
+
### Disclaimer
|
|
94
|
+
|
|
95
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
96
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
97
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
98
|
+
|
|
99
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
100
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
101
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
102
|
+
|
|
103
|
+
### Trademarks
|
|
104
|
+
|
|
105
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
106
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
107
|
+
trademarks, trade names, service marks or product names.
|
|
108
|
+
|
|
109
|
+
## Grant of Future License
|
|
110
|
+
|
|
111
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
112
|
+
the MIT license that is effective on the second anniversary of the date we make
|
|
113
|
+
the Software available. On or after that date, you may use the Software under
|
|
114
|
+
the MIT license, in which case the following will apply:
|
|
115
|
+
|
|
116
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
117
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
118
|
+
the Software without restriction, including without limitation the rights to
|
|
119
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
120
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
121
|
+
so, subject to the following conditions:
|
|
122
|
+
|
|
123
|
+
The above copyright notice and this permission notice shall be included in all
|
|
124
|
+
copies or substantial portions of the Software.
|
|
125
|
+
|
|
126
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
127
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
128
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
129
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
130
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
131
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
132
|
+
SOFTWARE.
|
package/dist/brand.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand colours for the places that CANNOT read a CSS variable.
|
|
3
|
+
*
|
|
4
|
+
* The palette's real home is `apps/dashboard/app/theme.css`, and anything
|
|
5
|
+
* rendering inside `.pc-theme` must keep using `var(--pc-primary)` and friends
|
|
6
|
+
* — those adapt per theme, these cannot. This file exists for the contexts
|
|
7
|
+
* where a custom property never resolves:
|
|
8
|
+
*
|
|
9
|
+
* - HTML email (no CSS variable support in most clients)
|
|
10
|
+
* - satori OG images (renders to a bitmap, no cascade)
|
|
11
|
+
* - downloadable artifacts: PWA manifests, icon files, the Caddy badge
|
|
12
|
+
* - pages served outside the dashboard's React tree (the app password gate)
|
|
13
|
+
* - `var(--token, fallback)` fallbacks, for the moment before the sheet loads
|
|
14
|
+
*
|
|
15
|
+
* Before this existed those contexts each kept their own literal, and they
|
|
16
|
+
* drifted: thirteen email templates, the password gate and the customer-facing
|
|
17
|
+
* manifest generator were all still painting `#2d6a4f` — a green that has never
|
|
18
|
+
* matched `--pc-primary` in any version of the v2 palette.
|
|
19
|
+
*
|
|
20
|
+
* `apps/dashboard/test/lib/brand-tokens.test.ts` parses theme.css and fails if
|
|
21
|
+
* any value here disagrees with it, so the two cannot drift apart again
|
|
22
|
+
* silently. If that test fails, theme.css is right and this file is stale.
|
|
23
|
+
*
|
|
24
|
+
* Colours only. The mark's geometry lives in `@percher/mark`, a private
|
|
25
|
+
* workspace package with its own proprietary LICENSE, because this package is
|
|
26
|
+
* published to npm and the artwork must not be inside anything that carries a
|
|
27
|
+
* copying grant. Hex values are not the thing being protected, and published
|
|
28
|
+
* `@percher/core/appstore-artifacts` needs `iconColourHex` to build the user's
|
|
29
|
+
* own PWA manifest, so the palette stays here and ships.
|
|
30
|
+
*/
|
|
31
|
+
export declare const BRAND: {
|
|
32
|
+
/** `--pc-primary`, light theme. The green in every email and manifest. */
|
|
33
|
+
readonly green: "#2e8055";
|
|
34
|
+
/** `--pc-primary`, dark theme. OG cards and the dark PWA icons are dark-only. */
|
|
35
|
+
readonly greenDark: "#4faa7d";
|
|
36
|
+
/** `--pc-accent`, light theme. */
|
|
37
|
+
readonly pink: "#d4004c";
|
|
38
|
+
/** `--pc-accent`, dark theme. */
|
|
39
|
+
readonly pinkDark: "#e26a8d";
|
|
40
|
+
/** `--pc-accent-bg` / `--pc-accent-text`, light — only needed as fallbacks. */
|
|
41
|
+
readonly pinkTint: "#fce8ee";
|
|
42
|
+
/** Ink on a light surface — `--pc-fg`. */
|
|
43
|
+
readonly ink: "#33291d";
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The owner-pickable icon colours, keyed by the value stored in the apps
|
|
47
|
+
* table's `icon_colour` column.
|
|
48
|
+
*
|
|
49
|
+
* NOTE the `terracotta` key. The colour it names became pink when the accent
|
|
50
|
+
* moved; the key did not, because it is persisted data and renaming it is a
|
|
51
|
+
* migration, not a rename. Read it as "the secondary accent" — the label shown
|
|
52
|
+
* in the picker is defined separately, and does say Pink.
|
|
53
|
+
*/
|
|
54
|
+
export declare const ICON_COLOURS: {
|
|
55
|
+
readonly green: "#2e8055";
|
|
56
|
+
readonly terracotta: "#d4004c";
|
|
57
|
+
readonly blue: "#4a6fa4";
|
|
58
|
+
readonly purple: "#6e4dba";
|
|
59
|
+
readonly olive: "#b8923a";
|
|
60
|
+
};
|
|
61
|
+
export type IconColourKey = keyof typeof ICON_COLOURS;
|
|
62
|
+
/** Resolve a stored `icon_colour` to a hex, falling back to the brand green. */
|
|
63
|
+
export declare function iconColourHex(token: string | null | undefined): string;
|
|
64
|
+
//# sourceMappingURL=brand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,eAAO,MAAM,KAAK;IAChB,0EAA0E;aAC1E,KAAK,EAAE,SAAS;IAChB,iFAAiF;aACjF,SAAS,EAAE,SAAS;IACpB,kCAAkC;aAClC,IAAI,EAAE,SAAS;IACf,iCAAiC;aACjC,QAAQ,EAAE,SAAS;IACnB,+EAA+E;aAC/E,QAAQ,EAAE,SAAS;IACnB,0CAA0C;aAC1C,GAAG,EAAE,SAAS;CACN,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY;aACvB,KAAK;aACL,UAAU;aACV,IAAI,EAAE,SAAS;aACf,MAAM,EAAE,SAAS;aACjB,KAAK,EAAE,SAAS;CACR,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,YAAY,CAAC;AAEtD,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGtE"}
|
package/dist/brand.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand colours for the places that CANNOT read a CSS variable.
|
|
3
|
+
*
|
|
4
|
+
* The palette's real home is `apps/dashboard/app/theme.css`, and anything
|
|
5
|
+
* rendering inside `.pc-theme` must keep using `var(--pc-primary)` and friends
|
|
6
|
+
* — those adapt per theme, these cannot. This file exists for the contexts
|
|
7
|
+
* where a custom property never resolves:
|
|
8
|
+
*
|
|
9
|
+
* - HTML email (no CSS variable support in most clients)
|
|
10
|
+
* - satori OG images (renders to a bitmap, no cascade)
|
|
11
|
+
* - downloadable artifacts: PWA manifests, icon files, the Caddy badge
|
|
12
|
+
* - pages served outside the dashboard's React tree (the app password gate)
|
|
13
|
+
* - `var(--token, fallback)` fallbacks, for the moment before the sheet loads
|
|
14
|
+
*
|
|
15
|
+
* Before this existed those contexts each kept their own literal, and they
|
|
16
|
+
* drifted: thirteen email templates, the password gate and the customer-facing
|
|
17
|
+
* manifest generator were all still painting `#2d6a4f` — a green that has never
|
|
18
|
+
* matched `--pc-primary` in any version of the v2 palette.
|
|
19
|
+
*
|
|
20
|
+
* `apps/dashboard/test/lib/brand-tokens.test.ts` parses theme.css and fails if
|
|
21
|
+
* any value here disagrees with it, so the two cannot drift apart again
|
|
22
|
+
* silently. If that test fails, theme.css is right and this file is stale.
|
|
23
|
+
*
|
|
24
|
+
* Colours only. The mark's geometry lives in `@percher/mark`, a private
|
|
25
|
+
* workspace package with its own proprietary LICENSE, because this package is
|
|
26
|
+
* published to npm and the artwork must not be inside anything that carries a
|
|
27
|
+
* copying grant. Hex values are not the thing being protected, and published
|
|
28
|
+
* `@percher/core/appstore-artifacts` needs `iconColourHex` to build the user's
|
|
29
|
+
* own PWA manifest, so the palette stays here and ships.
|
|
30
|
+
*/
|
|
31
|
+
export const BRAND = {
|
|
32
|
+
/** `--pc-primary`, light theme. The green in every email and manifest. */
|
|
33
|
+
green: "#2e8055",
|
|
34
|
+
/** `--pc-primary`, dark theme. OG cards and the dark PWA icons are dark-only. */
|
|
35
|
+
greenDark: "#4faa7d",
|
|
36
|
+
/** `--pc-accent`, light theme. */
|
|
37
|
+
pink: "#d4004c",
|
|
38
|
+
/** `--pc-accent`, dark theme. */
|
|
39
|
+
pinkDark: "#e26a8d",
|
|
40
|
+
/** `--pc-accent-bg` / `--pc-accent-text`, light — only needed as fallbacks. */
|
|
41
|
+
pinkTint: "#fce8ee",
|
|
42
|
+
/** Ink on a light surface — `--pc-fg`. */
|
|
43
|
+
ink: "#33291d",
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The owner-pickable icon colours, keyed by the value stored in the apps
|
|
47
|
+
* table's `icon_colour` column.
|
|
48
|
+
*
|
|
49
|
+
* NOTE the `terracotta` key. The colour it names became pink when the accent
|
|
50
|
+
* moved; the key did not, because it is persisted data and renaming it is a
|
|
51
|
+
* migration, not a rename. Read it as "the secondary accent" — the label shown
|
|
52
|
+
* in the picker is defined separately, and does say Pink.
|
|
53
|
+
*/
|
|
54
|
+
export const ICON_COLOURS = {
|
|
55
|
+
green: BRAND.green,
|
|
56
|
+
terracotta: BRAND.pink,
|
|
57
|
+
blue: "#4a6fa4",
|
|
58
|
+
purple: "#6e4dba",
|
|
59
|
+
olive: "#b8923a",
|
|
60
|
+
};
|
|
61
|
+
/** Resolve a stored `icon_colour` to a hex, falling back to the brand green. */
|
|
62
|
+
export function iconColourHex(token) {
|
|
63
|
+
if (!token)
|
|
64
|
+
return BRAND.green;
|
|
65
|
+
return ICON_COLOURS[token] ?? BRAND.green;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=brand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,0EAA0E;IAC1E,KAAK,EAAE,SAAS;IAChB,iFAAiF;IACjF,SAAS,EAAE,SAAS;IACpB,kCAAkC;IAClC,IAAI,EAAE,SAAS;IACf,iCAAiC;IACjC,QAAQ,EAAE,SAAS;IACnB,+EAA+E;IAC/E,QAAQ,EAAE,SAAS;IACnB,0CAA0C;IAC1C,GAAG,EAAE,SAAS;CACN,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;IAClB,UAAU,EAAE,KAAK,CAAC,IAAI;IACtB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;CACR,CAAC;AAIX,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IAC/B,OAAO,YAAY,CAAC,KAAsB,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for a signed, single-use client-IP attestation on the
|
|
3
|
+
* dashboard -> API hop.
|
|
4
|
+
*
|
|
5
|
+
* The API decides a request's rate-limit / audit IP from `X-Real-IP`, trusted
|
|
6
|
+
* only behind Caddy's `X-Percher-Edge` marker. That marker is a REUSABLE bearer:
|
|
7
|
+
* whoever holds it can assert any IP, on any request, forever. It is the right
|
|
8
|
+
* shape for Caddy (one hop, config-injected, nothing else to work with), and the
|
|
9
|
+
* wrong shape for a service forwarding an end user's IP — an observer of one
|
|
10
|
+
* cleartext control-network request could lift it and re-assert it with an IP of
|
|
11
|
+
* their choosing, forging attribution and smearing rate limits.
|
|
12
|
+
*
|
|
13
|
+
* So a service that HOLDS the internal secret proves the IP instead of bearing a
|
|
14
|
+
* token for it: an HMAC over (version, ip, timestamp). Three properties follow,
|
|
15
|
+
* and all three are the point:
|
|
16
|
+
*
|
|
17
|
+
* - it is bound to ONE ip — a captured attestation re-asserts that same
|
|
18
|
+
* address and nothing else, so it cannot be aimed at a victim;
|
|
19
|
+
* - it is bound to a timestamp — replay dies with the skew window, rather
|
|
20
|
+
* than lasting until the internal secret is rotated;
|
|
21
|
+
* - it never puts a long-lived credential on the wire at all.
|
|
22
|
+
*
|
|
23
|
+
* A service that holds NO secret (the hosted-MCP transport, deliberately) cannot
|
|
24
|
+
* sign, and keeps forwarding Caddy's marker; that hop is bounded by the
|
|
25
|
+
* per-bearer and per-IP limits instead. This module is the single source of the
|
|
26
|
+
* canonical string so the signer and the verifier cannot drift, and it is
|
|
27
|
+
* dependency-free (no crypto) so it bundles into the dashboard (Node) as well as
|
|
28
|
+
* the API (Bun) — the same shape as ./internal-sig.
|
|
29
|
+
*/
|
|
30
|
+
/** The asserted client IP. Meaningless without the proof header below. */
|
|
31
|
+
export declare const CLIENT_IP_HEADER = "X-Percher-Client-IP";
|
|
32
|
+
/** `<unix-ms>.<hex hmac-sha256>` over {@link clientIpAttestationPayload}. */
|
|
33
|
+
export declare const CLIENT_IP_PROOF_HEADER = "X-Percher-Client-IP-Proof";
|
|
34
|
+
/** Canonical-form version, so a verifier can refuse a shape it can't compare. */
|
|
35
|
+
export declare const CLIENT_IP_ATTESTATION_VERSION = "v1";
|
|
36
|
+
/**
|
|
37
|
+
* Replay window. Deliberately much tighter than the 5 minutes `/internal/*`
|
|
38
|
+
* signing allows: the signer and verifier are two containers on one host with
|
|
39
|
+
* no meaningful clock skew, and the only thing a replay buys is re-asserting an
|
|
40
|
+
* IP that genuinely just made a request — so there is no reason to leave the
|
|
41
|
+
* door open for minutes.
|
|
42
|
+
*/
|
|
43
|
+
export declare const CLIENT_IP_ATTESTATION_MAX_SKEW_MS = 60000;
|
|
44
|
+
/** Longest asserted IP accepted. An IPv6 address with a zone fits in 45. */
|
|
45
|
+
export declare const CLIENT_IP_MAX_LENGTH = 45;
|
|
46
|
+
export interface ClientIpAttestationParts {
|
|
47
|
+
/** The end user's address, exactly as it will be sent. */
|
|
48
|
+
ip: string;
|
|
49
|
+
/** The value placed in the proof header, stringified verbatim so the
|
|
50
|
+
* verifier signs the exact bytes — no Number round-trip. */
|
|
51
|
+
timestamp: number | string;
|
|
52
|
+
}
|
|
53
|
+
/** The exact string both the signer and the verifier feed to HMAC-SHA256. */
|
|
54
|
+
export declare function clientIpAttestationPayload(parts: ClientIpAttestationParts): string;
|
|
55
|
+
/** Assemble the proof header value. */
|
|
56
|
+
export declare function formatClientIpProof(timestamp: number | string, signature: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Split a proof header back into its parts, or null when it is not the
|
|
59
|
+
* `<digits>.<hex>` shape. Returning null (rather than throwing) keeps a
|
|
60
|
+
* malformed header indistinguishable from an absent one at the call site: both
|
|
61
|
+
* mean "not attested", which is the safe reading.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseClientIpProof(raw: string | null | undefined): {
|
|
64
|
+
timestamp: string;
|
|
65
|
+
signature: string;
|
|
66
|
+
} | null;
|
|
67
|
+
//# sourceMappingURL=client-ip-attestation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-ip-attestation.d.ts","sourceRoot":"","sources":["../src/client-ip-attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AACtD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,8BAA8B,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,wBAAwB;IACvC,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX;iEAC6D;IAC7D,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED,6EAA6E;AAC7E,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,wBAAwB,GAAG,MAAM,CAElF;AAED,uCAAuC;AACvC,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASjD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for a signed, single-use client-IP attestation on the
|
|
3
|
+
* dashboard -> API hop.
|
|
4
|
+
*
|
|
5
|
+
* The API decides a request's rate-limit / audit IP from `X-Real-IP`, trusted
|
|
6
|
+
* only behind Caddy's `X-Percher-Edge` marker. That marker is a REUSABLE bearer:
|
|
7
|
+
* whoever holds it can assert any IP, on any request, forever. It is the right
|
|
8
|
+
* shape for Caddy (one hop, config-injected, nothing else to work with), and the
|
|
9
|
+
* wrong shape for a service forwarding an end user's IP — an observer of one
|
|
10
|
+
* cleartext control-network request could lift it and re-assert it with an IP of
|
|
11
|
+
* their choosing, forging attribution and smearing rate limits.
|
|
12
|
+
*
|
|
13
|
+
* So a service that HOLDS the internal secret proves the IP instead of bearing a
|
|
14
|
+
* token for it: an HMAC over (version, ip, timestamp). Three properties follow,
|
|
15
|
+
* and all three are the point:
|
|
16
|
+
*
|
|
17
|
+
* - it is bound to ONE ip — a captured attestation re-asserts that same
|
|
18
|
+
* address and nothing else, so it cannot be aimed at a victim;
|
|
19
|
+
* - it is bound to a timestamp — replay dies with the skew window, rather
|
|
20
|
+
* than lasting until the internal secret is rotated;
|
|
21
|
+
* - it never puts a long-lived credential on the wire at all.
|
|
22
|
+
*
|
|
23
|
+
* A service that holds NO secret (the hosted-MCP transport, deliberately) cannot
|
|
24
|
+
* sign, and keeps forwarding Caddy's marker; that hop is bounded by the
|
|
25
|
+
* per-bearer and per-IP limits instead. This module is the single source of the
|
|
26
|
+
* canonical string so the signer and the verifier cannot drift, and it is
|
|
27
|
+
* dependency-free (no crypto) so it bundles into the dashboard (Node) as well as
|
|
28
|
+
* the API (Bun) — the same shape as ./internal-sig.
|
|
29
|
+
*/
|
|
30
|
+
/** The asserted client IP. Meaningless without the proof header below. */
|
|
31
|
+
export const CLIENT_IP_HEADER = "X-Percher-Client-IP";
|
|
32
|
+
/** `<unix-ms>.<hex hmac-sha256>` over {@link clientIpAttestationPayload}. */
|
|
33
|
+
export const CLIENT_IP_PROOF_HEADER = "X-Percher-Client-IP-Proof";
|
|
34
|
+
/** Canonical-form version, so a verifier can refuse a shape it can't compare. */
|
|
35
|
+
export const CLIENT_IP_ATTESTATION_VERSION = "v1";
|
|
36
|
+
/**
|
|
37
|
+
* Replay window. Deliberately much tighter than the 5 minutes `/internal/*`
|
|
38
|
+
* signing allows: the signer and verifier are two containers on one host with
|
|
39
|
+
* no meaningful clock skew, and the only thing a replay buys is re-asserting an
|
|
40
|
+
* IP that genuinely just made a request — so there is no reason to leave the
|
|
41
|
+
* door open for minutes.
|
|
42
|
+
*/
|
|
43
|
+
export const CLIENT_IP_ATTESTATION_MAX_SKEW_MS = 60_000;
|
|
44
|
+
/** Longest asserted IP accepted. An IPv6 address with a zone fits in 45. */
|
|
45
|
+
export const CLIENT_IP_MAX_LENGTH = 45;
|
|
46
|
+
/** The exact string both the signer and the verifier feed to HMAC-SHA256. */
|
|
47
|
+
export function clientIpAttestationPayload(parts) {
|
|
48
|
+
return [CLIENT_IP_ATTESTATION_VERSION, parts.ip, String(parts.timestamp)].join("\n");
|
|
49
|
+
}
|
|
50
|
+
/** Assemble the proof header value. */
|
|
51
|
+
export function formatClientIpProof(timestamp, signature) {
|
|
52
|
+
return `${timestamp}.${signature}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Split a proof header back into its parts, or null when it is not the
|
|
56
|
+
* `<digits>.<hex>` shape. Returning null (rather than throwing) keeps a
|
|
57
|
+
* malformed header indistinguishable from an absent one at the call site: both
|
|
58
|
+
* mean "not attested", which is the safe reading.
|
|
59
|
+
*/
|
|
60
|
+
export function parseClientIpProof(raw) {
|
|
61
|
+
if (!raw)
|
|
62
|
+
return null;
|
|
63
|
+
const dot = raw.indexOf(".");
|
|
64
|
+
if (dot <= 0 || dot === raw.length - 1)
|
|
65
|
+
return null;
|
|
66
|
+
const timestamp = raw.slice(0, dot);
|
|
67
|
+
const signature = raw.slice(dot + 1);
|
|
68
|
+
if (!/^\d{1,15}$/.test(timestamp))
|
|
69
|
+
return null;
|
|
70
|
+
if (!/^[0-9a-f]{64}$/i.test(signature))
|
|
71
|
+
return null;
|
|
72
|
+
return { timestamp, signature };
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=client-ip-attestation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-ip-attestation.js","sourceRoot":"","sources":["../src/client-ip-attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AACtD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,sBAAsB,GAAG,2BAA2B,CAAC;AAElE,iFAAiF;AACjF,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,MAAM,CAAC;AAExD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAUvC,6EAA6E;AAC7E,MAAM,UAAU,0BAA0B,CAAC,KAA+B;IACxE,OAAO,CAAC,6BAA6B,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,mBAAmB,CAAC,SAA0B,EAAE,SAAiB;IAC/E,OAAO,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAA8B;IAE9B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
export { APP_NAME_RE } from "./app-name.js";
|
|
2
|
+
export { BRAND, ICON_COLOURS, type IconColourKey, iconColourHex } from "./brand.js";
|
|
3
|
+
export { CLIENT_IP_ATTESTATION_MAX_SKEW_MS, CLIENT_IP_ATTESTATION_VERSION, CLIENT_IP_HEADER, CLIENT_IP_MAX_LENGTH, CLIENT_IP_PROOF_HEADER, type ClientIpAttestationParts, clientIpAttestationPayload, formatClientIpProof, parseClientIpProof, } from "./client-ip-attestation.js";
|
|
2
4
|
export { INTERNAL_SIG_HEADER, INTERNAL_SIG_MAX_SKEW_MS, INTERNAL_SIG_VERSION, INTERNAL_TS_HEADER, type InternalSigParts, internalSigPayload, } from "./internal-sig.js";
|
|
3
|
-
export {
|
|
5
|
+
export { CURRENT_TERMS_VERSION, LEGAL_DOCUMENT_HASHES, LEGAL_DOCUMENT_LAST_UPDATED, LEGAL_DOCUMENT_VERSIONS, type TermsAcceptanceEvidence, } from "./legal.js";
|
|
6
|
+
export { canonicalLegalChangeJson, compareVersions, declaredVersion, LEGAL_CHANGE_ID_PATTERN, LEGAL_CHANGES, LEGAL_VERSION_PATTERN, type LegalChange, type LegalChangeDocument, type LegalChangeEnforcement, type LegalChangeReason, type LegalDocumentKey, MATERIAL_CHANGE_NOTICE_MS, openMaterialChanges, pendingAcknowledgement, SIGNUP_DISCLOSURE_HEADER, } from "./legal-changes.js";
|
|
7
|
+
export { MAX_ANON_TARBALL_BYTES, MAX_INLINE_PUBLISH_BYTES, MAX_OPEN_UPLOAD_SESSIONS_PER_USER, MAX_TARBALL_BYTES, MAX_UPLOAD_CHUNK_BYTES, MAX_UPLOAD_SESSION_FILES, MIN_UPLOAD_CHUNK_BYTES, UPLOAD_SESSION_MAX_LIFETIME_MS, UPLOAD_SESSION_TTL_MS, } from "./limits.js";
|
|
4
8
|
export { MAX_LOG_SEARCH_LENGTH } from "./logs.js";
|
|
9
|
+
export { MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS, normaliseOAuthClientName, } from "./oauth-client-name.js";
|
|
10
|
+
export { describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes } from "./oauth-scopes.js";
|
|
5
11
|
export { DEFAULT_DOMAIN_SUFFIX, DEFAULT_MCP_HTTP_PORT, DEFAULT_POCKETBASE_IMAGE, MCP_TOOL_COUNT_HOSTED, MCP_TOOL_COUNT_LOCAL, } from "./platform.js";
|
|
6
12
|
export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env.js";
|
|
7
13
|
export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime.js";
|
|
8
|
-
export {
|
|
14
|
+
export { normalizeSignupInviteCode, SIGNUP_INVITE_CODE_ALPHABET, SIGNUP_INVITE_CODE_LENGTH, SIGNUP_INVITE_CODE_PREFIX, SIGNUP_INVITE_HEADER, } from "./signup-invite.js";
|
|
15
|
+
export { BUN_MAX_IDLE_TIMEOUT_SECONDS, HTTP_IDLE_TIMEOUT_SECONDS, type HttpIdleTimeoutSurface, TIMEOUTS, type TimeoutKey, } from "./timeouts.js";
|
|
9
16
|
export { WAITLIST_THROTTLE_WINDOW_MS } from "./waitlist.js";
|
|
10
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,yCAAyC,EACzC,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,KAAK,sBAAsB,EAC3B,QAAQ,EACR,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
export { APP_NAME_RE } from "./app-name.js";
|
|
2
|
+
export { BRAND, ICON_COLOURS, iconColourHex } from "./brand.js";
|
|
3
|
+
export { CLIENT_IP_ATTESTATION_MAX_SKEW_MS, CLIENT_IP_ATTESTATION_VERSION, CLIENT_IP_HEADER, CLIENT_IP_MAX_LENGTH, CLIENT_IP_PROOF_HEADER, clientIpAttestationPayload, formatClientIpProof, parseClientIpProof, } from "./client-ip-attestation.js";
|
|
2
4
|
export { INTERNAL_SIG_HEADER, INTERNAL_SIG_MAX_SKEW_MS, INTERNAL_SIG_VERSION, INTERNAL_TS_HEADER, internalSigPayload, } from "./internal-sig.js";
|
|
3
|
-
export {
|
|
5
|
+
export { CURRENT_TERMS_VERSION, LEGAL_DOCUMENT_HASHES, LEGAL_DOCUMENT_LAST_UPDATED, LEGAL_DOCUMENT_VERSIONS, } from "./legal.js";
|
|
6
|
+
export { canonicalLegalChangeJson, compareVersions, declaredVersion, LEGAL_CHANGE_ID_PATTERN, LEGAL_CHANGES, LEGAL_VERSION_PATTERN, MATERIAL_CHANGE_NOTICE_MS, openMaterialChanges, pendingAcknowledgement, SIGNUP_DISCLOSURE_HEADER, } from "./legal-changes.js";
|
|
7
|
+
export { MAX_ANON_TARBALL_BYTES, MAX_INLINE_PUBLISH_BYTES, MAX_OPEN_UPLOAD_SESSIONS_PER_USER, MAX_TARBALL_BYTES, MAX_UPLOAD_CHUNK_BYTES, MAX_UPLOAD_SESSION_FILES, MIN_UPLOAD_CHUNK_BYTES, UPLOAD_SESSION_MAX_LIFETIME_MS, UPLOAD_SESSION_TTL_MS, } from "./limits.js";
|
|
4
8
|
export { MAX_LOG_SEARCH_LENGTH } from "./logs.js";
|
|
9
|
+
export { MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS, normaliseOAuthClientName, } from "./oauth-client-name.js";
|
|
10
|
+
export { describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes } from "./oauth-scopes.js";
|
|
5
11
|
export { DEFAULT_DOMAIN_SUFFIX, DEFAULT_MCP_HTTP_PORT, DEFAULT_POCKETBASE_IMAGE, MCP_TOOL_COUNT_HOSTED, MCP_TOOL_COUNT_LOCAL, } from "./platform.js";
|
|
6
12
|
export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env.js";
|
|
7
13
|
export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime.js";
|
|
8
|
-
export {
|
|
14
|
+
export { normalizeSignupInviteCode, SIGNUP_INVITE_CODE_ALPHABET, SIGNUP_INVITE_CODE_LENGTH, SIGNUP_INVITE_CODE_PREFIX, SIGNUP_INVITE_HEADER, } from "./signup-invite.js";
|
|
15
|
+
export { BUN_MAX_IDLE_TIMEOUT_SECONDS, HTTP_IDLE_TIMEOUT_SECONDS, TIMEOUTS, } from "./timeouts.js";
|
|
9
16
|
export { WAITLIST_THROTTLE_WINDOW_MS } from "./waitlist.js";
|
|
10
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAElB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAsB,aAAa,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EAEtB,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAElB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,uBAAuB,GAExB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EAMrB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,yCAAyC,EACzC,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EAEzB,QAAQ,GAET,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { LEGAL_DOCUMENT_VERSIONS } from "./legal.js";
|
|
2
|
+
/**
|
|
3
|
+
* The declared history of changes to the published legal documents.
|
|
4
|
+
*
|
|
5
|
+
* The Terms and the AUP promise every account holder an email at least 30 days
|
|
6
|
+
* before a material change takes effect, and a free way out during that window.
|
|
7
|
+
* This array is the machinery behind that promise: the notifier job, the public
|
|
8
|
+
* updates list and the review-time guard all read it, so a delivered email, a
|
|
9
|
+
* published page and the document's own `**Version:**` header cannot disagree.
|
|
10
|
+
*
|
|
11
|
+
* Materiality is a person's decision recorded in a reviewed commit. Nothing here
|
|
12
|
+
* is derived from a diff, and there is deliberately no heuristic: the only way
|
|
13
|
+
* that judgement can fail is by quietly downgrading a change to someone's rights
|
|
14
|
+
* into a typo fix, and a classifier would fail in exactly that direction.
|
|
15
|
+
*
|
|
16
|
+
* An entry is frozen once its notice has been sent. Editing it would make a
|
|
17
|
+
* delivered email untrue, so the job stores a hash on first send and refuses to
|
|
18
|
+
* send more notices for that change if the hash stops matching.
|
|
19
|
+
*/
|
|
20
|
+
/** Why we are permitted to make the change. Annex 1(j) of 93/13 wants this stated. */
|
|
21
|
+
export type LegalChangeReason = "legal_compliance" | "security" | "abuse_prevention" | "service_change" | "provider_change" | "clarification";
|
|
22
|
+
/**
|
|
23
|
+
* How the change binds.
|
|
24
|
+
*
|
|
25
|
+
* `notice_only` is the default and the only tier the platform implements:
|
|
26
|
+
* notice, a free exit, and continued use on or after the effective date is
|
|
27
|
+
* acceptance of the amended terms. The other two exist so that a change needing
|
|
28
|
+
* affirmative agreement has somewhere to live rather than being smuggled through
|
|
29
|
+
* the silence path. `consent_required` in particular covers anything resting on
|
|
30
|
+
* a GDPR consent basis, where silence is categorically invalid, and anything
|
|
31
|
+
* touching price or the subject matter of the contract.
|
|
32
|
+
*/
|
|
33
|
+
export type LegalChangeEnforcement = "notice_only" | "acknowledge_required" | "consent_required";
|
|
34
|
+
export type LegalDocumentKey = keyof typeof LEGAL_DOCUMENT_VERSIONS;
|
|
35
|
+
export interface LegalChangeDocument {
|
|
36
|
+
key: LegalDocumentKey;
|
|
37
|
+
/** The version this document carried before the change. */
|
|
38
|
+
from: string;
|
|
39
|
+
/** The version it carries after. Must equal LEGAL_DOCUMENT_VERSIONS[key] for the latest entry. */
|
|
40
|
+
to: string;
|
|
41
|
+
}
|
|
42
|
+
export interface LegalChange {
|
|
43
|
+
/** `YYYY-MM-DD-slug`. The date half is the effective date, so an id reads as a promise. */
|
|
44
|
+
id: string;
|
|
45
|
+
/** The human decision. An editorial change never sends an email. */
|
|
46
|
+
materiality: "material" | "editorial";
|
|
47
|
+
reason: LegalChangeReason;
|
|
48
|
+
enforcement: LegalChangeEnforcement;
|
|
49
|
+
/** ISO instant, UTC. When the notice may start going out. */
|
|
50
|
+
announceAt: string;
|
|
51
|
+
/** ISO instant, UTC. When the new documents govern. */
|
|
52
|
+
effectiveAt: string;
|
|
53
|
+
/** Every document whose version header moves in this change. */
|
|
54
|
+
documents: readonly LegalChangeDocument[];
|
|
55
|
+
/**
|
|
56
|
+
* Authored plain text, both languages, rendered escaped. A Swedish operator
|
|
57
|
+
* publishing Swedish documents may not send an English-only notice about them.
|
|
58
|
+
*/
|
|
59
|
+
summary: {
|
|
60
|
+
en: string;
|
|
61
|
+
sv: string;
|
|
62
|
+
};
|
|
63
|
+
/** Set when the same change also modifies the Service itself (2019/770 Art. 19). */
|
|
64
|
+
modifiesService?: boolean;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Newest last. Empty because version 1.0 is the first publication under
|
|
68
|
+
* Kaizensu AB: there is no earlier version to have changed from, so the chain
|
|
69
|
+
* starts here rather than being backfilled with a notice nobody was owed.
|
|
70
|
+
*/
|
|
71
|
+
export declare const LEGAL_CHANGES: readonly LegalChange[];
|
|
72
|
+
/**
|
|
73
|
+
* Header carrying the change ids the signup form actually rendered.
|
|
74
|
+
*
|
|
75
|
+
* Same channel as the terms-acceptance header, for the same reason: the signup
|
|
76
|
+
* flow returns no session when email verification is required, so a follow-up
|
|
77
|
+
* authenticated POST would be refused and the evidence lost. Comma-separated,
|
|
78
|
+
* and never trusted on its own — the server intersects it with what it can
|
|
79
|
+
* independently see was disclosable.
|
|
80
|
+
*/
|
|
81
|
+
export declare const SIGNUP_DISCLOSURE_HEADER = "x-percher-disclosed-changes";
|
|
82
|
+
/** The notice period the documents promise, in milliseconds. */
|
|
83
|
+
export declare const MATERIAL_CHANGE_NOTICE_MS: number;
|
|
84
|
+
/** `id` shape: the date half is parsed, so this is a gate and not decoration. */
|
|
85
|
+
export declare const LEGAL_CHANGE_ID_PATTERN: RegExp;
|
|
86
|
+
/** Versions are `1.0` or `1.0.1`; an editorial change may only move the last component. */
|
|
87
|
+
export declare const LEGAL_VERSION_PATTERN: RegExp;
|
|
88
|
+
/**
|
|
89
|
+
* The canonical JSON of an entry, for the hash that freezes it after the first
|
|
90
|
+
* send. Keys are emitted in a fixed order rather than insertion order so that
|
|
91
|
+
* reformatting the source cannot invalidate a change already announced.
|
|
92
|
+
*/
|
|
93
|
+
export declare function canonicalLegalChangeJson(change: LegalChange): string;
|
|
94
|
+
/** Entries whose notice may go out now: material, announced, newest last. */
|
|
95
|
+
export declare function openMaterialChanges(now: number, changes?: readonly LegalChange[]): LegalChange[];
|
|
96
|
+
/**
|
|
97
|
+
* The latest declared version of a document, or its current published version
|
|
98
|
+
* when no change has ever named it.
|
|
99
|
+
*/
|
|
100
|
+
export declare function declaredVersion(key: LegalDocumentKey, changes?: readonly LegalChange[]): string;
|
|
101
|
+
/**
|
|
102
|
+
* Order two `1.0` / `1.0.1` strings. Unparseable input sorts as older, so a
|
|
103
|
+
* version we cannot read never suppresses a notice.
|
|
104
|
+
*/
|
|
105
|
+
export declare function compareVersions(left: string, right: string): number;
|
|
106
|
+
/**
|
|
107
|
+
* The change a user has not yet acknowledged, if any.
|
|
108
|
+
*
|
|
109
|
+
* Read by the dashboard banner and by the operator dry-run. Deliberately NOT
|
|
110
|
+
* read by the API's auth middleware: a stale accepted version is never an
|
|
111
|
+
* authorization failure. Extending the 403 to a version comparison would break
|
|
112
|
+
* every customer's deploy pipeline and every agent integration on a chosen
|
|
113
|
+
* date, with the remedy being a browser click a CI runner cannot perform — and
|
|
114
|
+
* it would be stricter than the Terms promise, which make continued use the
|
|
115
|
+
* acceptance. A test in the API asserts that absence.
|
|
116
|
+
*/
|
|
117
|
+
export declare function pendingAcknowledgement(acceptedTermsVersion: string | null | undefined, now: number, changes?: readonly LegalChange[]): LegalChange | null;
|
|
118
|
+
//# sourceMappingURL=legal-changes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legal-changes.d.ts","sourceRoot":"","sources":["../src/legal-changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AAEH,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,UAAU,GACV,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,eAAe,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAEjG,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,uBAAuB,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,2FAA2F;IAC3F,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,WAAW,EAAE,UAAU,GAAG,WAAW,CAAC;IACtC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;IACpC,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C;;;OAGG;IACH,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,oFAAoF;IACpF,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAAO,CAAC;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,QAA2B,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,QAAiD,CAAC;AAEtF,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,QAAyB,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAgBpE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,EAAE,CAIf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,gBAAgB,EACrB,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6BnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,GAAG,IAAI,CAiBpB"}
|