@pyric/cli 0.1.0-alpha.30 → 0.1.0-alpha.30.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/dist/assurance/.generated/conformance-verdicts.d.ts +8 -0
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +8 -0
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/server/peer.d.ts +10 -0
- package/dist/bridge/server/peer.d.ts.map +1 -1
- package/dist/bridge/server/peer.js +46 -1
- package/dist/bridge/server/peer.js.map +1 -1
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +13 -2
- package/dist/cli/serve.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +2 -2
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +1 -1
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
- package/dist/conformance/.generated/conformance-docs.js +1 -1
- package/dist/conformance/.generated/conformance-docs.js.map +1 -1
- package/dist/register/app-bridge-admin-app-check.d.ts +3 -0
- package/dist/register/app-bridge-admin-app-check.d.ts.map +1 -0
- package/dist/register/app-bridge-admin-app-check.js +43 -0
- package/dist/register/app-bridge-admin-app-check.js.map +1 -0
- package/dist/register/app-bridge-app-check.d.ts +4 -0
- package/dist/register/app-bridge-app-check.d.ts.map +1 -1
- package/dist/register/app-bridge-app-check.js +4 -0
- package/dist/register/app-bridge-app-check.js.map +1 -1
- package/dist/register/app-bridge-shadow.d.ts +4 -2
- package/dist/register/app-bridge-shadow.d.ts.map +1 -1
- package/dist/register/app-bridge-shadow.js +29 -5
- package/dist/register/app-bridge-shadow.js.map +1 -1
- package/dist/register/index.d.ts.map +1 -1
- package/dist/register/index.js +21 -0
- package/dist/register/index.js.map +1 -1
- package/dist/serve/ai-diagnostics-relay.d.ts +4 -1
- package/dist/serve/ai-diagnostics-relay.d.ts.map +1 -1
- package/dist/serve/ai-diagnostics-relay.js +3 -0
- package/dist/serve/ai-diagnostics-relay.js.map +1 -1
- package/dist/serve/ai-proxy.d.ts +21 -1
- package/dist/serve/ai-proxy.d.ts.map +1 -1
- package/dist/serve/ai-proxy.js +39 -14
- package/dist/serve/ai-proxy.js.map +1 -1
- package/dist/serve/ai-terminal-blocks.d.ts +6 -4
- package/dist/serve/ai-terminal-blocks.d.ts.map +1 -1
- package/dist/serve/ai-terminal-blocks.js +7 -4
- package/dist/serve/ai-terminal-blocks.js.map +1 -1
- package/dist/serve/ai-terminal-text.d.ts +8 -0
- package/dist/serve/ai-terminal-text.d.ts.map +1 -1
- package/dist/serve/ai-terminal-text.js +10 -0
- package/dist/serve/ai-terminal-text.js.map +1 -1
- package/dist/serve/bundler.d.ts +40 -0
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +133 -2
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +28 -26
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.d.ts +4 -4
- package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.js +8 -52
- package/dist/serve/entries/app-ai-passthrough.js.map +1 -1
- package/dist/serve/firebase-module-swap.d.ts +22 -0
- package/dist/serve/firebase-module-swap.d.ts.map +1 -1
- package/dist/serve/firebase-module-swap.js +51 -3
- package/dist/serve/firebase-module-swap.js.map +1 -1
- package/dist/serve/gemini-error-redaction.d.ts +6 -0
- package/dist/serve/gemini-error-redaction.d.ts.map +1 -0
- package/dist/serve/gemini-error-redaction.js +61 -0
- package/dist/serve/gemini-error-redaction.js.map +1 -0
- package/dist/serve/gemini-proxy.d.ts +40 -0
- package/dist/serve/gemini-proxy.d.ts.map +1 -0
- package/dist/serve/gemini-proxy.js +105 -0
- package/dist/serve/gemini-proxy.js.map +1 -0
- package/dist/serve/hosted/method-protocol.d.ts +4 -4
- package/dist/serve/html-injection.d.ts +2 -0
- package/dist/serve/html-injection.d.ts.map +1 -1
- package/dist/serve/html-injection.js +4 -2
- package/dist/serve/html-injection.js.map +1 -1
- package/dist/serve/namespace.d.ts +9 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +71 -15
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +3 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +1 -0
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +29 -10
- package/dist/serve/site-ui/docs/ai-compat.md +30 -9
- package/dist/serve/site-ui/docs/app-check-compat/index.html +42 -18
- package/dist/serve/site-ui/docs/app-check-compat.md +42 -18
- package/dist/serve/site-ui/docs/auth-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/auth-compat.md +4 -4
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +21 -5
- package/dist/serve/site-ui/docs/build/ai-logic.md +23 -2
- package/dist/serve/site-ui/docs/database-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/database-compat.md +4 -4
- package/dist/serve/site-ui/docs/firestore-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/firestore-compat.md +4 -4
- package/dist/serve/site-ui/docs/index.json +10 -0
- package/dist/serve/site-ui/docs/messaging-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/messaging-compat.md +4 -4
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +4 -0
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +5 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api/index.html +2 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api.md +3 -0
- package/dist/serve/site-ui/docs/storage-compat/index.html +5 -5
- package/dist/serve/site-ui/docs/storage-compat.md +5 -5
- package/dist/serve/site-ui/docs/tutorial/index.html +11 -1
- package/dist/serve/site-ui/docs/tutorial.md +12 -1
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +2 -0
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/ai-proxy-paths.d.ts +32 -0
- package/dist/serve/worker/ai-proxy-paths.d.ts.map +1 -0
- package/dist/serve/worker/ai-proxy-paths.js +20 -0
- package/dist/serve/worker/ai-proxy-paths.js.map +1 -0
- package/dist/serve/worker/client/ai.d.ts +3 -1
- package/dist/serve/worker/client/ai.d.ts.map +1 -1
- package/dist/serve/worker/client/ai.js +3 -0
- package/dist/serve/worker/client/ai.js.map +1 -1
- package/dist/serve/worker/entry.js +2 -0
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host-ai.d.ts +4 -5
- package/dist/serve/worker/host-ai.d.ts.map +1 -1
- package/dist/serve/worker/host-ai.js +19 -23
- package/dist/serve/worker/host-ai.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +13 -4
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/host-context.js.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.d.ts.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.js +11 -0
- package/dist/serve/worker/inbound-validation/operation-arguments.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +13 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/package.json +4 -4
- package/src/assurance/.generated/conformance-verdicts.ts +8 -0
- package/src/bridge/server/peer.ts +49 -1
- package/src/cli/serve.ts +14 -2
- package/src/conformance/.generated/can-i-use-browser.ts +2 -2
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/register/app-bridge-admin-app-check.ts +46 -0
- package/src/register/app-bridge-app-check.ts +4 -0
- package/src/register/app-bridge-shadow.ts +35 -5
- package/src/register/index.ts +23 -0
- package/src/serve/ai-diagnostics-relay.ts +6 -1
- package/src/serve/ai-proxy.ts +43 -14
- package/src/serve/ai-terminal-blocks.ts +8 -4
- package/src/serve/ai-terminal-text.ts +11 -0
- package/src/serve/bundler.ts +163 -2
- package/src/serve/entries/ai.ts +28 -27
- package/src/serve/entries/app-ai-passthrough.ts +8 -58
- package/src/serve/firebase-module-swap.ts +52 -3
- package/src/serve/gemini-error-redaction.ts +60 -0
- package/src/serve/gemini-proxy.ts +140 -0
- package/src/serve/html-injection.ts +4 -2
- package/src/serve/namespace.ts +74 -15
- package/src/serve/sandbox-session.ts +4 -0
- package/src/serve/vite-sandbox-generation.ts +2 -0
- package/src/serve/worker/ai-proxy-paths.ts +35 -0
- package/src/serve/worker/client/ai.ts +6 -1
- package/src/serve/worker/entry.ts +2 -0
- package/src/serve/worker/host-ai.ts +23 -23
- package/src/serve/worker/host-context.ts +13 -3
- package/src/serve/worker/inbound-validation/operation-arguments.ts +11 -0
- package/src/serve/worker/protocol.ts +14 -2
|
@@ -24,7 +24,7 @@ html{scrollbar-gutter:stable}
|
|
|
24
24
|
</details>
|
|
25
25
|
<details class="compat-row" data-status="ok">
|
|
26
26
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">new CustomProvider({ getToken })</code><span class="compat-sub"><span class="compat-behavior">A <code>CustomProvider</code> constructs and is accepted by <code>initializeAppCheck</code>. With a debug token in effect, the SDK never calls it (a provider whose <code>getToken</code> would reject is called zero times).</span></span></span></summary>
|
|
27
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>customProviderCalls</code>; the capture initialized App Check with a <code>CustomProvider</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
27
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>customProviderCalls</code>; the capture initialized App Check with a <code>CustomProvider</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
28
28
|
</details>
|
|
29
29
|
<details class="compat-row" data-status="unverified">
|
|
30
30
|
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">new ReCaptchaV3Provider(siteKey); new ReCaptchaEnterpriseProvider(siteKey)</code><span class="compat-sub"><span class="compat-behavior">Both reCAPTCHA providers construct from a site key and are accepted by <code>initializeAppCheck</code>.</span></span></span></summary>
|
|
@@ -35,19 +35,19 @@ html{scrollbar-gutter:stable}
|
|
|
35
35
|
<div class="compat-list">
|
|
36
36
|
<details class="compat-row" data-status="ok">
|
|
37
37
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getToken(appCheck) on an app without initializeAppCheck</code><span class="compat-sub"><span class="compat-behavior">Rejects with a <code>FirebaseError</code> with code <code>appCheck/use-before-activation</code>, <code>customData.appName</code>, and the message telling the caller to call <code>initializeAppCheck()</code> first.</span></span></span></summary>
|
|
38
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-get-token-before-initialize.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-get-token-before-initialize</code></a> (<code>publicGetTokenWithUninitializedApp</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L148" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
38
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-get-token-before-initialize.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-get-token-before-initialize</code></a> (<code>publicGetTokenWithUninitializedApp</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L148" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
39
39
|
</details>
|
|
40
40
|
<details class="compat-row" data-status="ok">
|
|
41
41
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getToken(appCheck, forceRefresh?): Promise<AppCheckTokenResult></code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token }</code> where <code>token</code> is a three-segment JWT: header <code>alg</code> RS256, <code>typ</code> JWT, with a <code>kid</code>; claims <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>; <code>iss</code> is <code>https://firebaseappcheck.googleapis.com/<projectNumber></code>, <code>aud</code> is <code>[projects/<projectNumber>, projects/<projectId>]</code>, <code>sub</code> is the app id, and <code>exp - iat</code> is 3600 seconds.</span></span></span></summary>
|
|
42
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>first</code>, <code>forced</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
42
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>first</code>, <code>forced</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
43
43
|
</details>
|
|
44
44
|
<details class="compat-row" data-status="ok">
|
|
45
45
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getToken(appCheck, forceRefresh?)</code><span class="compat-sub"><span class="compat-behavior">A second <code>getToken</code> returns the cached token; <code>getToken(appCheck, true)</code> returns a different, newly minted token.</span></span></span></summary>
|
|
46
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>secondEqualsFirst</code>, <code>forceRefreshDiffersFromFirst</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
46
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>secondEqualsFirst</code>, <code>forceRefreshDiffersFromFirst</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
47
47
|
</details>
|
|
48
48
|
<details class="compat-row" data-status="ok">
|
|
49
49
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getLimitedUseToken(appCheck): Promise<AppCheckTokenResult></code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token }</code> with the same JWT shape as <code>getToken</code>, a different token on every call, never the cached token, and leaves the cached <code>getToken</code> token unchanged.</span></span></span></summary>
|
|
50
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-limited-use-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-limited-use-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L181" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
50
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-limited-use-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-limited-use-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L181" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
51
51
|
</details>
|
|
52
52
|
<details class="compat-row" data-status="unsupported">
|
|
53
53
|
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">getToken / getLimitedUseToken when attestation fails</code><span class="compat-sub"><span class="compat-behavior">When the exchange is refused (an unregistered debug token), both reject with a <code>FirebaseError</code> with code <code>appCheck/fetch-status-error</code> and <code>customData.httpStatus</code> 403.</span></span></span></summary>
|
|
@@ -58,15 +58,15 @@ html{scrollbar-gutter:stable}
|
|
|
58
58
|
<div class="compat-list">
|
|
59
59
|
<details class="compat-row" data-status="ok">
|
|
60
60
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onTokenChanged(appCheck, listener | observer): Unsubscribe</code><span class="compat-sub"><span class="compat-behavior">Returns an unsubscribe function. A listener registered after a token exists is not called synchronously; it is called once, asynchronously, with the current cached token.</span></span></span></summary>
|
|
61
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (<code>unsubscribeType</code>, <code>lateListenerCallsSynchronous</code>, <code>lateListenerCallsWithin500ms</code>, <code>lateListenerTokenSequence</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L205" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
61
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (<code>unsubscribeType</code>, <code>lateListenerCallsSynchronous</code>, <code>lateListenerCallsWithin500ms</code>, <code>lateListenerTokenSequence</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L205" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
62
62
|
</details>
|
|
63
63
|
<details class="compat-row" data-status="diverged">
|
|
64
64
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">onTokenChanged(appCheck, listener | observer)</code><span class="compat-sub"><span class="compat-behavior">Production, debug mode: listeners and observers registered before the first <code>getToken</code> are never called, not by the first token, a forced refresh, or limited-use tokens. Sandbox: they are called once each time the cached token changes (the first token and every forced refresh), never for limited-use tokens, and not after unsubscribing, which is the upstream-documented behavior for an attestation provider.</span></span></span></summary>
|
|
65
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
65
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
66
66
|
</details>
|
|
67
67
|
<details class="compat-row" data-status="ok">
|
|
68
68
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setTokenAutoRefreshEnabled(appCheck, isEnabled): void</code><span class="compat-sub"><span class="compat-behavior">Returns <code>undefined</code> and does not throw for either value; <code>getToken</code> still returns the cached token afterwards. Background refresh timing is not captured.</span></span></span></summary>
|
|
69
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-auto-refresh-toggle.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-auto-refresh-toggle</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L263" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
69
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-auto-refresh-toggle.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-auto-refresh-toggle</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L263" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L43" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
70
70
|
</details>
|
|
71
71
|
</div>
|
|
72
72
|
<h2 id="token-exchange-on-the-wire">Token exchange on the wire</h2>
|
|
@@ -80,31 +80,47 @@ html{scrollbar-gutter:stable}
|
|
|
80
80
|
<div class="compat-list">
|
|
81
81
|
<details class="compat-row" data-status="ok">
|
|
82
82
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().createToken(appId)</code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token, ttlMillis: 3600000 }</code>; the token has the web SDK token shape (RS256 JWT with <code>kid</code>, claims <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>, <code>exp - iat</code> 3600).</span></span></span></summary>
|
|
83
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-shape.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-shape</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
83
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-shape.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-shape</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L70" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
84
84
|
</details>
|
|
85
85
|
<details class="compat-row" data-status="ok">
|
|
86
86
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().createToken(appId, { ttlMillis })</code><span class="compat-sub"><span class="compat-behavior"><code>ttlMillis</code> of 30 minutes is honored in the result and in <code>exp - iat</code>; 29 minutes and 7 days plus one second reject with <code>app-check/invalid-argument</code> and the message that names the 30 minute to 7 day range.</span></span></span></summary>
|
|
87
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-custom-ttl.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-custom-ttl</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
87
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-custom-ttl.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-custom-ttl</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L105" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
88
88
|
</details>
|
|
89
89
|
<details class="compat-row" data-status="diverged">
|
|
90
90
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().createToken(unknownAppId)</code><span class="compat-sub"><span class="compat-behavior">Production: an app id the project does not own rejects with <code>app-check/unknown-error</code> and the message <code>App not registered: <appId>.</code> Sandbox: it has no list of the project's registered apps, so it mints a token for any non-empty app id; an empty app id rejects with <code>app-check/invalid-argument</code>.</span></span></span></summary>
|
|
91
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
91
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L113" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
92
92
|
</details>
|
|
93
93
|
<details class="compat-row" data-status="ok">
|
|
94
94
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token)</code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ appId, token }</code> for an Admin-minted token and for a token from the web debug exchange; the decoded token carries <code>app_id</code>, <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>, and no <code>alreadyConsumed</code> key is present.</span></span></span></summary>
|
|
95
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
95
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L126" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
96
96
|
</details>
|
|
97
97
|
<details class="compat-row" data-status="ok">
|
|
98
98
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(badToken)</code><span class="compat-sub"><span class="compat-behavior">A token with an altered signature rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has invalid signature.</code>; a non-JWT string and the empty string reject with <code>app-check/invalid-argument</code> and the decoding message.</span></span></span></summary>
|
|
99
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-tampered.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-tampered</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
99
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-tampered.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-tampered</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L143" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
100
100
|
</details>
|
|
101
101
|
<details class="compat-row" data-status="unverified">
|
|
102
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a <code>kid</code>
|
|
102
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a token with no <code>kid</code> is checked against every key. A <code>kid</code> that names a key other than the sandbox's marks a token from Firebase: in sandbox AI mode it rejects with Pyric's message (row <code>verify-token-firebase-token</code>), and in production AI mode it goes to firebase-admin (row <code>verify-token-firebase-token-production-ai-mode</code>). After the signature, a non-numeric or future <code>nbf</code> and a non-numeric <code>exp</code> reject as an invalid signature; an absent <code>nbf</code> or <code>exp</code> does not apply.</span></span></span></summary>
|
|
103
103
|
<div class="compat-evidence"><div class="compat-probe">Upstream-documented only: firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code> (<code>verifyToken</code>, <code>verifyContent</code>, <code>ensureProjectId</code>, <code>mapJwtErrorToAppCheckError</code>), <code>lib/utils/jwt.js</code> (<code>getKeyCallback</code>, <code>PublicKeySignatureVerifier.verify</code>), jws 4 <code>verify-stream.js</code> (<code>JWS_REGEX</code>), and jsonwebtoken 9 <code>verify.js</code> (<code>nbf</code>, <code>exp</code>). No capture; verification runs locally in the Admin SDK, so a capture would need no production change.</div></div>
|
|
104
104
|
</details>
|
|
105
105
|
<details class="compat-row" data-status="ok">
|
|
106
106
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, { consume: true })</code><span class="compat-sub"><span class="compat-behavior">The first consume resolves with <code>alreadyConsumed: false</code>, a second consume with <code>alreadyConsumed: true</code>, and a plain verify afterwards has no <code>alreadyConsumed</code> key.</span></span></span></summary>
|
|
107
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
107
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L197" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
108
|
+
</details>
|
|
109
|
+
<details class="compat-row" data-status="diverged">
|
|
110
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) in sandbox AI mode</code><span class="compat-sub"><span class="compat-behavior">Production verifies the App Check tokens Firebase issues. Sandbox: in sandbox AI mode, or in a process not started with <code>@pyric/cli/register</code> in production AI mode, a token whose <code>kid</code> names a key other than the sandbox's rejects with <code>app-check/invalid-argument</code> and Pyric's message, which says the token was not minted by the Pyric sandbox and to start the server in production AI mode (<code>PYRIC_AI_MODE=production</code>). The project check comes first, as in production.</span></span></span></summary>
|
|
111
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> (production verifies a token from its debug exchange); the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
112
|
+
</details>
|
|
113
|
+
<details class="compat-row" data-status="unverified">
|
|
114
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) under @pyric/cli/register in production AI mode</code><span class="compat-sub"><span class="compat-behavior">A token whose <code>kid</code> names a key other than the sandbox's goes to firebase-admin's own <code>verifyToken</code>, for an app scoped to the project the mirror resolves. It resolves with production's <code>{ appId, token }</code> shape, the decoded token carrying <code>app_id</code>, and firebase-admin's errors (signature, audience, issuer, expiry) pass through with their codes and messages. Sandbox tokens, tokens with no <code>kid</code>, and malformed tokens keep the sandbox path.</span></span></span></summary>
|
|
115
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> for the result shape; the verification itself is firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code>, run unchanged.</div></div>
|
|
116
|
+
</details>
|
|
117
|
+
<details class="compat-row" data-status="diverged">
|
|
118
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken, { consume: true }) in production AI mode</code><span class="compat-sub"><span class="compat-behavior">Production records consumption on Firebase's servers, once per project: the first consume resolves with <code>alreadyConsumed: false</code> and a second with <code>alreadyConsumed: true</code>. Sandbox: firebase-admin verifies the token without <code>consume</code>, and the mirror records it in the process, in the set it keeps for sandbox tokens, with the same <code>false</code> then <code>true</code> results.</span></span></span></summary>
|
|
119
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>; the sandbox side is pinned in the admin suite and the production AI mode app scenario. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L198" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
120
|
+
</details>
|
|
121
|
+
<details class="compat-row" data-status="unverified">
|
|
122
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">initializeApp({ credential: cert(serviceAccount) }), then getAppCheck().createToken and verifyToken</code><span class="compat-sub"><span class="compat-behavior">An app initialized with only a cert credential finds its project in the service account's <code>project_id</code> (or <code>projectId</code>), so <code>createToken</code> mints a token whose <code>aud</code> names <code>projects/<project_id></code> and <code>verifyToken</code> accepts it. The <code>projectId</code> app option wins over the credential, and the credential wins over <code>GOOGLE_CLOUD_PROJECT</code> and <code>GCLOUD_PROJECT</code>. The project id is not copied into <code>app.options</code>.</span></span></span></summary>
|
|
123
|
+
<div class="compat-evidence"><div class="compat-probe">Upstream-documented only: firebase-admin 13.6.0 <code>lib/utils/index.js</code> (<code>getExplicitProjectId</code>) and <code>lib/app/credential-internal.js</code> (<code>ServiceAccount</code>, <code>copyAttr</code>). No capture; the project is resolved locally in the Admin SDK.</div></div>
|
|
108
124
|
</details>
|
|
109
125
|
</div>
|
|
110
126
|
<h2 id="current-gaps">Current gaps</h2>
|
|
@@ -112,9 +128,13 @@ html{scrollbar-gutter:stable}
|
|
|
112
128
|
<p>Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.</p>
|
|
113
129
|
<div class="compat-list">
|
|
114
130
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">onTokenChanged(appCheck, listener | observer)</code><span class="compat-sub"><span class="compat-behavior">Production, debug mode: listeners and observers registered before the first <code>getToken</code> are never called, not by the first token, a forced refresh, or limited-use tokens. Sandbox: they are called once each time the cached token changes (the first token and every forced refresh), never for limited-use tokens, and not after unsubscribing, which is the upstream-documented behavior for an attestation provider.</span></span></span></summary>
|
|
115
|
-
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
131
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
|
|
116
132
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().createToken(unknownAppId)</code><span class="compat-sub"><span class="compat-behavior">Production: an app id the project does not own rejects with <code>app-check/unknown-error</code> and the message <code>App not registered: <appId>.</code> Sandbox: it has no list of the project's registered apps, so it mints a token for any non-empty app id; an empty app id rejects with <code>app-check/invalid-argument</code>.</span></span></span></summary>
|
|
117
|
-
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
133
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L113" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
134
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) in sandbox AI mode</code><span class="compat-sub"><span class="compat-behavior">Production verifies the App Check tokens Firebase issues. Sandbox: in sandbox AI mode, or in a process not started with <code>@pyric/cli/register</code> in production AI mode, a token whose <code>kid</code> names a key other than the sandbox's rejects with <code>app-check/invalid-argument</code> and Pyric's message, which says the token was not minted by the Pyric sandbox and to start the server in production AI mode (<code>PYRIC_AI_MODE=production</code>). The project check comes first, as in production.</span></span></span></summary>
|
|
135
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> (production verifies a token from its debug exchange); the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
136
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken, { consume: true }) in production AI mode</code><span class="compat-sub"><span class="compat-behavior">Production records consumption on Firebase's servers, once per project: the first consume resolves with <code>alreadyConsumed: false</code> and a second with <code>alreadyConsumed: true</code>. Sandbox: firebase-admin verifies the token without <code>consume</code>, and the mirror records it in the process, in the set it keeps for sandbox tokens, with the same <code>false</code> then <code>true</code> results.</span></span></span></summary>
|
|
137
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>; the sandbox side is pinned in the admin suite and the production AI mode app scenario. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L198" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
118
138
|
</div>
|
|
119
139
|
<h3 id="not-implemented-yet">Not implemented yet</h3>
|
|
120
140
|
<p>Tracked behavior that is not implemented in the current contract.</p>
|
|
@@ -129,6 +149,10 @@ html{scrollbar-gutter:stable}
|
|
|
129
149
|
<div class="compat-list">
|
|
130
150
|
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">new ReCaptchaV3Provider(siteKey); new ReCaptchaEnterpriseProvider(siteKey)</code><span class="compat-sub"><span class="compat-behavior">Both reCAPTCHA providers construct from a site key and are accepted by <code>initializeAppCheck</code>.</span></span></span></summary>
|
|
131
151
|
<div class="compat-evidence"><div class="compat-note">Upstream-documented only (firebase 12.13.0 typings and the App Check web guide). The oracle web app has no reCAPTCHA registration, so no capture exists; the sandbox never loads reCAPTCHA.</div></div></details>
|
|
132
|
-
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a <code>kid</code>
|
|
152
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a token with no <code>kid</code> is checked against every key. A <code>kid</code> that names a key other than the sandbox's marks a token from Firebase: in sandbox AI mode it rejects with Pyric's message (row <code>verify-token-firebase-token</code>), and in production AI mode it goes to firebase-admin (row <code>verify-token-firebase-token-production-ai-mode</code>). After the signature, a non-numeric or future <code>nbf</code> and a non-numeric <code>exp</code> reject as an invalid signature; an absent <code>nbf</code> or <code>exp</code> does not apply.</span></span></span></summary>
|
|
133
153
|
<div class="compat-evidence"><div class="compat-note">Upstream-documented only: firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code> (<code>verifyToken</code>, <code>verifyContent</code>, <code>ensureProjectId</code>, <code>mapJwtErrorToAppCheckError</code>), <code>lib/utils/jwt.js</code> (<code>getKeyCallback</code>, <code>PublicKeySignatureVerifier.verify</code>), jws 4 <code>verify-stream.js</code> (<code>JWS_REGEX</code>), and jsonwebtoken 9 <code>verify.js</code> (<code>nbf</code>, <code>exp</code>). No capture; verification runs locally in the Admin SDK, so a capture would need no production change.</div></div></details>
|
|
154
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) under @pyric/cli/register in production AI mode</code><span class="compat-sub"><span class="compat-behavior">A token whose <code>kid</code> names a key other than the sandbox's goes to firebase-admin's own <code>verifyToken</code>, for an app scoped to the project the mirror resolves. It resolves with production's <code>{ appId, token }</code> shape, the decoded token carrying <code>app_id</code>, and firebase-admin's errors (signature, audience, issuer, expiry) pass through with their codes and messages. Sandbox tokens, tokens with no <code>kid</code>, and malformed tokens keep the sandbox path.</span></span></span></summary>
|
|
155
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> for the result shape; the verification itself is firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code>, run unchanged.</div></div></details>
|
|
156
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">initializeApp({ credential: cert(serviceAccount) }), then getAppCheck().createToken and verifyToken</code><span class="compat-sub"><span class="compat-behavior">An app initialized with only a cert credential finds its project in the service account's <code>project_id</code> (or <code>projectId</code>), so <code>createToken</code> mints a token whose <code>aud</code> names <code>projects/<project_id></code> and <code>verifyToken</code> accepts it. The <code>projectId</code> app option wins over the credential, and the credential wins over <code>GOOGLE_CLOUD_PROJECT</code> and <code>GCLOUD_PROJECT</code>. The project id is not copied into <code>app.options</code>.</span></span></span></summary>
|
|
157
|
+
<div class="compat-evidence"><div class="compat-note">Upstream-documented only: firebase-admin 13.6.0 <code>lib/utils/index.js</code> (<code>getExplicitProjectId</code>) and <code>lib/app/credential-internal.js</code> (<code>ServiceAccount</code>, <code>copyAttr</code>). No capture; the project is resolved locally in the Admin SDK.</div></div></details>
|
|
134
158
|
</div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#initializeappcheckapp-options-and-providers" data-astro-cid-mw7aashj>initializeAppCheck(app, options) and providers</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tokens-gettoken-getlimitedusetoken" data-astro-cid-mw7aashj>Tokens: getToken, getLimitedUseToken</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#listeners-and-refresh-ontokenchanged-settokenautorefreshenabled" data-astro-cid-mw7aashj>Listeners and refresh: onTokenChanged, setTokenAutoRefreshEnabled</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#token-exchange-on-the-wire" data-astro-cid-mw7aashj>Token exchange on the wire</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#firebase-admin-getappcheck" data-astro-cid-mw7aashj>firebase-admin getAppCheck()</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#not-implemented-yet" data-astro-cid-mw7aashj>Not implemented yet</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#unverified" data-astro-cid-mw7aashj>Unverified</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
|