Cometchat native production
Skill cometchat/cometchat-skills/skills/cometchat-native-production
Add CometChat chat & messaging and voice & video calls to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.
npx -y skills add cometchat/cometchat-skills --skill cometchat-native-productionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
What its author says it does
Copied from the file, not written here
Production-readiness for React Native — server-minted auth tokens, user management CRUD, external-backend recipes (Express / Hono / Firebase Functions / Vercel Serverless). RN has no API routes, so the backend is always external.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
24.6 KB, ~6.2k tokens by cl100k_base, as published. Nobody here has run it
Purpose
Teaches Claude how to move a React Native CometChat integration from dev-mode Auth Key to production-ready server-minted auth tokens + user CRUD. Covers:
- Why the dev
authKeycan't ship to production - Auth Key vs REST API Key — which lives where
- Server endpoint recipes (Express / Hono / Firebase Functions / Vercel)
- Client-side:
CometChatUIKit.login({ authToken })+ token refresh - User CRUD endpoints + auth-provider integration (Firebase Auth / Supabase / Clerk / Auth0)
- Security checklist + rate limits
Read cometchat-native-core first (init/login/wrapper chain) before this skill — production just swaps one prop on the provider, but understanding the provider lifecycle is the prerequisite.
Ground truth: docs/ui-kit/react-native/methods.mdx, and the cross-platform REST API at https://{APP_ID}.api-{REGION}.cometchat.io/v3/. Official docs: https://www.cometchat.com/docs/fundamentals/user-auth · Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP).
1. Why production auth matters
In dev mode, CometChatUIKit.login({ uid: "..." }) uses the authKey passed to CometChatUIKit.init({ authKey }). That key is embedded in your React Native bundle. For a signed iOS .ipa or Android .apk/.aab, anyone can extract it with standard reverse-engineering tools (unzip, strings, apktool, ReverseAPK) and use it to log in as ANY user in your CometChat app — read private messages, send as other users, access every conversation.
Production MUST use server-side token generation:
- Your server holds the REST API Key (a different key from the client Auth Key).
- On user login, your server calls CometChat's REST API with the REST API Key to mint a short-lived Auth Token for that specific UID.
- Your client receives the Auth Token and calls
CometChatUIKit.login({ authToken }). - If the token leaks, the blast radius is one user session, not your whole app.
Exactly the same threat model as JWTs for a REST API. If you've built a login flow before, this is that.
2. Auth Key vs REST API Key — two different keys
Easy to confuse. Both come from the CometChat Dashboard (your app → API & Auth Keys), but they live in different places and have different privileges.
| Key | Where in dashboard | Purpose | Where it lives |
|---|---|---|---|
| Auth Key | "Auth Keys" table | Client-side SDK login({ uid }) in dev mode | Client bundle — dev only. Never in production builds. |
| REST API Key | "REST API Keys" table | Server-to-server: token generation, user CRUD, custom-message-send | Server only. Never in an RN bundle, app.json extra, EXPO_PUBLIC_* var, or git-committed file. |
If the project only has an Auth Key, the user needs to generate a REST API Key in the dashboard: API & Auth Keys → REST API Keys → Add Key. Pick "Full Access" for server-side use.
3. The token auth pattern (4 steps)
1. Client logs into YOUR auth (Firebase Auth / Supabase / Clerk / Auth0 / custom)
↓
2. Client asks YOUR backend for a CometChat auth token
↓ (POST /api/cometchat-token { uid })
3. Backend calls CometChat REST API → gets an Auth Token for that UID
↓ POST https://{APP_ID}.api-{REGION}.cometchat.io/v3/users/{uid}/auth_tokens
with header apiKey: <REST_API_KEY>
↓
4. Client calls CometChatUIKit.login({ authToken: "..." })
The RN client never sees the REST API Key. The server never ships a password or email to the client. The CometChat SDK holds the auth token, not a static key.
4. Server endpoint recipes
RN projects don't have Next.js-style API routes. You need a separate backend. Pick the one the user already has, or the simplest if they're starting fresh.
4a. Express (Node.js backend)
// server/routes/cometchat-token.ts
import { Router } from "express";
import { requireAuth } from "../middleware/auth"; // your existing auth
const router = Router();
const APP_ID = process.env.COMETCHAT_APP_ID!;
const REGION = process.env.COMETCHAT_REGION!;
const REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;
router.post("/cometchat-token", requireAuth, async (req, res) => {
// Derive UID from authenticated session — NOT from the request body in prod.
const uid = req.user.id;
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify({}),
},
);
if (!r.ok) {
const error = await r.text();
console.error("CometChat token error:", error);
return res.status(r.status).json({ error: "Failed to generate auth token" });
}
const data = await r.json();
return res.json({ authToken: data.data.authToken });
});
export default router;
4b. Hono (Cloudflare Workers / Bun / Node)
// server/cometchat-token.ts
import { Hono } from "hono";
const app = new Hono();
app.post("/api/cometchat-token", async (c) => {
const user = c.get("user"); // your middleware-resolved user
if (!user) return c.json({ error: "unauthorized" }, 401);
const APP_ID = c.env.COMETCHAT_APP_ID;
const REGION = c.env.COMETCHAT_REGION;
const REST_API_KEY = c.env.COMETCHAT_REST_API_KEY;
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(user.id)}/auth_tokens`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify({}),
},
);
if (!r.ok) return c.json({ error: "token mint failed" }, 502);
const data = await r.json();
return c.json({ authToken: data.data.authToken });
});
export default app;
4c. Firebase Cloud Functions
// functions/src/cometchat-token.ts
import { onCall, HttpsError } from "firebase-functions/v2/https";
export const getCometChatToken = onCall(
{ secrets: ["COMETCHAT_APP_ID", "COMETCHAT_REGION", "COMETCHAT_REST_API_KEY"] },
async (request) => {
if (!request.auth) throw new HttpsError("unauthenticated", "Sign in required");
const uid = request.auth.uid;
const APP_ID = process.env.COMETCHAT_APP_ID!;
const REGION = process.env.COMETCHAT_REGION!;
const REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify({}),
},
);
if (!r.ok) throw new HttpsError("internal", "token mint failed");
const data = await r.json();
return { authToken: data.data.authToken };
},
);
Client call:
import functions from "@react-native-firebase/functions";
const result = await functions().httpsCallable("getCometChatToken")();
const authToken = result.data.authToken;
4d. Vercel Serverless / Next.js API Route
Even if the RN app isn't Next.js, the user's existing web app often is. Reuse the same backend:
// pages/api/cometchat-token.ts (or app/api/cometchat-token/route.ts)
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth";
import { authOptions } from "./auth/[...nextauth]";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") return res.status(405).end();
const session = await getServerSession(req, res, authOptions);
if (!session?.user) return res.status(401).json({ error: "unauthorized" });
const APP_ID = process.env.COMETCHAT_APP_ID!;
const REGION = process.env.COMETCHAT_REGION!;
const REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;
const uid = session.user.id;
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,
{
method: "POST",
headers: { "Content-Type": "application/json", appId: APP_ID, apiKey: REST_API_KEY },
body: JSON.stringify({}),
},
);
if (!r.ok) return res.status(502).json({ error: "token mint failed" });
const data = await r.json();
return res.json({ authToken: data.data.authToken });
}
5. Client-side: CometChatUIKit.login({ authToken })
Once the server is serving tokens, update the RN client to fetch the token and use it. This is a change to the CometChatProvider (see cometchat-native-core § 6) — swap the uid prop for an authToken prop.
5a. Update the provider to support authToken
// CometChatProvider.tsx — production-aware version
import React, { createContext, useContext, useEffect, useState, type ReactNode } from "react";
import { CometChatUIKit } from "@cometchat/chat-uikit-react-native";
let initialized = false;
let loginInFlight: Promise<unknown> | null = null;
async function ensureLoggedIn(authToken?: string, uid?: string): Promise<void> {
// getLoggedInUser() THROWS { code: "NOT_FOUND" } when there's no session — it does NOT
// return null. An uncaught throw here leaves the app stuck on splash (see core §2/§6).
let existing;
try {
existing = await CometChatUIKit.getLoggedInUser();
} catch (e: any) {
if (e?.code !== "NOT_FOUND") throw e; // no-session is the expected first-run path
}
if (existing) return;
if (loginInFlight) {
await loginInFlight;
return;
}
// Production — prefer authToken
if (authToken) {
loginInFlight = CometChatUIKit.login({ authToken });
} else if (uid) {
loginInFlight = CometChatUIKit.login({ uid }); // dev fallback
} else {
return; // nothing to log in with yet
}
try {
await loginInFlight;
} finally {
loginInFlight = null;
}
}
interface Props {
appId: string;
region: string;
authKey?: string; // dev only; omit in production
authToken?: string; // production — from your backend
uid?: string; // dev only
children: ReactNode;
}
export function CometChatProvider({ appId, region, authKey, authToken, uid, children }: Props) {
const [isReady, setIsReady] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function setup() {
try {
if (!initialized) {
initialized = true;
await CometChatUIKit.init({
appId,
region,
subscriptionType: "ALL_USERS",
...(authKey ? { authKey } : {}),
});
}
await ensureLoggedIn(authToken, uid);
setIsReady(true);
} catch (e) {
setError(String(e));
}
}
setup();
}, [appId, region, authKey, authToken, uid]);
if (!isReady) return null;
return <>{children}</>;
}
Push registration lands here — right after ensureLoggedIn resolves,
before setIsReady(true). The CometChat SDK scopes push tokens to the
logged-in user, so registering before login associates the token with
"anonymous" and the device won't receive pushes.
import { bootstrapPushAfterLogin } from "../push/bootstrap";
//...
await ensureLoggedIn(authToken, uid);
await bootstrapPushAfterLogin(); // registers FCM/APNs token with CometChat
setIsReady(true);
And unregister BEFORE CometChatUIKit.logout() — the SDK needs the user
context to dissociate the token. See cometchat-native-push § 7 for
the full bootstrapPushAfterLogin / unregisterPushTokenOnLogout helper
pair.
5b. Fetch the token from your backend
Typical app flow:
// App.tsx
import { useState, useEffect } from "react";
import { CometChatProvider } from "./src/providers/CometChatProvider";
import { useMyAppAuth } from "./src/hooks/useMyAppAuth"; // your existing auth
export default function App() {
const { user, isAuthenticated } = useMyAppAuth();
const [cometChatToken, setCometChatToken] = useState<string | null>(null);
useEffect(() => {
if (!isAuthenticated) {
setCometChatToken(null);
return;
}
// Fetch a CometChat auth token from your backend
fetch("https://api.yourapp.com/cometchat-token", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${user.jwt}` },
})
.then((r) => r.json())
.then((data) => setCometChatToken(data.authToken))
.catch((e) => console.error("CometChat token fetch failed:", e));
}, [isAuthenticated, user?.jwt]);
if (!isAuthenticated) return <LoginScreen />;
if (!cometChatToken) return <LoadingScreen message="Connecting chat..." />;
return (
<CometChatProvider
appId={COMETCHAT_APP_ID}
region={COMETCHAT_REGION}
authToken={cometChatToken}
// no authKey prop in production
>
<AppNavigator />
</CometChatProvider>
);
}
5c. Handle token expiry / refresh
Auth tokens have a configurable TTL (default 24 hours). On token expiry, SDK calls start failing. Handle this by re-minting on 401:
useEffect(() => {
const LISTENER_ID = "TOKEN_EXPIRY_LISTENER";
CometChat.addConnectionListener(
LISTENER_ID,
new CometChat.ConnectionListener({
onDisconnected: async () => {
// Connection dropped. Token might be expired.
// Re-fetch and re-login.
const freshToken = await fetchCometChatToken(user.jwt);
setCometChatToken(freshToken);
await CometChatUIKit.login({ authToken: freshToken });
},
}),
);
return () => CometChat.removeConnectionListener(LISTENER_ID);
}, [user?.jwt]);
A simpler approach for apps that can tolerate a forced re-login: on any 401 from the SDK, log the user out and force them through your app's sign-in flow again.
6. User management CRUD
When someone signs up in your app, you need to create a matching CometChat user. Same for profile updates (name/avatar change) and deletion. These happen on your backend, using the REST API with the REST API Key.
6a. Create a user on signup
async function createCometChatUser(uid: string, name: string, avatarUrl?: string) {
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify({
uid,
name,
avatar: avatarUrl,
}),
},
);
if (!r.ok) throw new Error(`CometChat user create failed: ${await r.text()}`);
return r.json();
}
6b. Update a user on profile change
async function updateCometChatUser(uid: string, updates: Partial<{ name: string; avatar: string; metadata: any }>) {
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify(updates),
},
);
if (!r.ok) throw new Error(`CometChat user update failed: ${await r.text()}`);
return r.json();
}
6c. Delete a user on account deletion
async function deleteCometChatUser(uid: string) {
const r = await fetch(
`https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
appId: APP_ID,
apiKey: REST_API_KEY,
},
body: JSON.stringify({ permanent: true }),
},
);
if (!r.ok) throw new Error(`CometChat user delete failed: ${await r.text()}`);
}
6d. Where to wire these calls
The CRUD functions live on your backend; you call them from your existing auth event handlers:
| Auth event | When to call | Function |
|---|---|---|
| User signs up | After your app's user creation succeeds | createCometChatUser(newUser.id, newUser.name, newUser.avatarUrl) |
| User updates name or avatar | After your app's profile update succeeds | updateCometChatUser(user.id, { name, avatar }) |
| User deletes account | Before/after your app's user deletion | deleteCometChatUser(user.id) |
7. Auth-provider integration recipes
Your RN app's auth layer typically comes from one of these SDKs. How to wire CometChat into each:
7a. Firebase Auth
Firebase issues a UID per user. Use that same UID in CometChat.
// Backend (Firebase Cloud Function)
import { onDocumentCreated } from "firebase-functions/v2/firestore";
import { onUserCreated, onUserDeleted } from "firebase-functions/v2/auth";
export const onSignup = onUserCreated(async (event) => {
const { uid, displayName, photoURL } = event.data;
await createCometChatUser(uid, displayName ?? "User", photoURL);
});
export const onAccountDelete = onUserDeleted(async (event) => {
await deleteCometChatUser(event.data.uid);
});
// Profile updates are app-level — hook into your profile-update handler
Client — get the Firebase ID token, send to your cometchat-token endpoint:
import auth from "@react-native-firebase/auth";
const idToken = await auth().currentUser!.getIdToken();
const r = await fetch("/api/cometchat-token", {
method: "POST",
headers: { Authorization: `Bearer ${idToken}` },
});
const { authToken } = await r.json();
7b. Supabase Auth
Supabase also issues a UID. Use it as the CometChat UID.
// Backend — Supabase Edge Function triggered on signup
Deno.serve(async (req) => {
const event = await req.json();
if (event.type === "INSERT" && event.table === "users") {
const { id, email } = event.record;
await createCometChatUser(id, email.split("@")[0]);
}
return new Response("ok");
});
Client:
import { supabase } from "./supabase";
const { data: { session } } = await supabase.auth.getSession();
const r = await fetch("/api/cometchat-token", {
method: "POST",
headers: { Authorization: `Bearer ${session!.access_token}` },
});
7c. Clerk Expo
Clerk is Expo-friendly and has its own webhooks for user lifecycle events.
// Client — React Native
import { useAuth } from "@clerk/clerk-expo";
const { getToken, userId } = useAuth();
const jwt = await getToken();
const r = await fetch("/api/cometchat-token", {
method: "POST",
headers: { Authorization: `Bearer ${jwt}` },
});
Backend — use a Clerk webhook to trigger CRUD on user lifecycle events.
7d. Auth0
import { useAuth0 } from "react-native-auth0";
const { getCredentials } = useAuth0();
const { accessToken } = await getCredentials();
const r = await fetch("/api/cometchat-token", {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}` },
});
Backend — use Auth0 Actions or Rules to trigger CRUD webhooks.
7e. Custom JWT / bespoke auth
If the user's auth is custom (their own JWT), the pattern is the same: client includes Authorization: Bearer <jwt>, server validates + extracts UID + mints CometChat auth token.
8. Environment variables — split between client + server
| Variable | Location | Visibility |
|---|---|---|
COMETCHAT_APP_ID | Client AND server | OK client-side |
COMETCHAT_REGION | Client AND server | OK client-side |
COMETCHAT_AUTH_KEY | Dev client only. Remove from production. | Should NEVER ship in a production RN bundle |
COMETCHAT_REST_API_KEY | Server only. Your backend's env. | Never ships to client, ever |
COMETCHAT_TOKEN_ENDPOINT | Client | Your backend URL (e.g. https://api.yourapp.com/cometchat-token) — safe in client bundle |
Production RN client .env (or app.json extra):
COMETCHAT_APP_ID=your_app_id
COMETCHAT_REGION=us
COMETCHAT_TOKEN_ENDPOINT=https://api.yourapp.com/cometchat-token
# No COMETCHAT_AUTH_KEY in production
# No COMETCHAT_REST_API_KEY — server-only
Server .env:
COMETCHAT_APP_ID=your_app_id
COMETCHAT_REGION=us
COMETCHAT_REST_API_KEY=your_rest_api_key
9. Security checklist
Before releasing to production, verify:
-
COMETCHAT_AUTH_KEYremoved from client.env/app.json extra/ anyEXPO_PUBLIC_*var - Production provider uses
authTokenprop, notauthKey -
COMETCHAT_REST_API_KEYlives only on your backend (check withgrep -r REST_API_KEY src/) - Token endpoint is behind auth — unauthenticated users can't mint a token for an arbitrary UID
- UID derivation on the token endpoint comes from the authenticated session, NOT from the request body (otherwise anyone can mint a token for anyone)
- Rate limit on the token endpoint (prevents abuse)
- HTTPS-only — no HTTP in production
- User CRUD endpoints are authenticated (or called from webhooks with signature verification)
- CometChat user deletion happens on account deletion (GDPR / privacy compliance)
10. Rate limits + retry
CometChat's REST API has rate limits per app. For the token endpoint:
- Default: 100 requests/minute per app
- Token generation is cheap — if you're hitting limits, you're likely minting too often (e.g. one mint per RN screen mount). Mint once per sign-in, cache client-side, reuse until expiry.
Retry policy:
- 5xx — retry with exponential backoff (1s, 2s, 4s, give up)
- 4xx — do NOT retry. Surface the error.
11. Anti-patterns
-
NEVER ship the REST API Key in an RN bundle. Not under any env-var name or prefix. Not in
app.json extra. Not inEXPO_PUBLIC_*. Not in a .gitignored file the user commits by accident. If you see yourself writing an env var forREST_API_KEYin a client-side config, stop. -
NEVER let the client specify the UID to mint a token for. The server must derive UID from the authenticated session. A
POST /cometchat-token { uid: "..." }that trusts the body is equivalent to no auth — anyone can impersonate anyone. -
Don't cache the auth token to disk forever. It expires. Either re-mint on every cold start or store with a short TTL and refresh on 401.
-
Don't use
login({ uid })in production.uidmode requires an Auth Key on the UIKit settings. In production you should set neitherauthKeyon the UIKitSettings nor calllogin({ uid })— both are dev-only patterns. -
Don't forget user CRUD. A user who signs up in your app but has no matching CometChat user will get "user does not exist" errors on
login({ authToken }). The token endpoint mints tokens, but the user must already exist in CometChat. -
Don't skip the security checklist. Production bugs in auth are catastrophic.
-
Don't retry 4xx errors. Token-endpoint 400s are config mistakes (wrong REST API Key, malformed UID, etc.). Retrying makes it worse.
12. Verifying production auth works
- Build a production-configuration version of the app (no Auth Key, only AppId + Region + token endpoint).
- Log in as a real user through your app's normal flow.
- In the RN debugger network tab, confirm
POST /cometchat-tokenreturns{ authToken: "..." }. - Confirm
CometChatUIKit.login({ authToken })resolves. - Send a message — verify delivery.
- Force-close the app, reopen — token fetch + login should happen again on cold start.
- (Optional) Wait out the token TTL (default 24hr), verify the 401-refresh path works.
If any step fails, see cometchat-native-troubleshooting § Auth / Token issues.
Skill routing reference
| Skill | When to route |
|---|---|
cometchat-native-core | Init / login / provider wrapper chain — prerequisite |
cometchat-native-components | The base component props (nothing production-specific there) |
cometchat-native-placement | Where your chat UI goes (no change in production) |
cometchat-native-expo-patterns | Expo-specific env var wiring (expo-constants vs EXPO_PUBLIC_*) |
cometchat-native-bare-patterns | Bare RN env var wiring (react-native-config) |
cometchat-native-theming | Theme customization (independent of auth) |
cometchat-native-features | Feature flags (polls, extensions, etc. — all still work in prod) |
cometchat-native-customization | If customization depends on server-side data (user tags, metadata) |
cometchat-native-production | This skill — server tokens + user CRUD |
cometchat-native-troubleshooting | 401 on token fetch, "user does not exist" on login, token-endpoint rate limit |
Gives 0 of the 12 instructions most containers cloud skills give in ~6.2k tokens
Counted across 607 of the 657 authors here whose files we hold, read 2026-08-07
- run containers as a non-root userin 66 of 607, across 46 files
- use multi-stage buildsin 53 of 607, across 44 files
- use Promise.all for independent operationsin 47 of 607, across 13 files
- import directly instead of barrel filesin 46 of 607, across 12 files
- use ternary instead of AND for conditionalsin 45 of 607, across 12 files
- use Set or Map for O(1) lookupsin 42 of 607, across 10 files
- create a .dockerignore filein 41 of 607, across 31 files
- Read individual rule files for detailsin 39 of 607, across 9 files
- copy dependency files before source codein 36 of 607, across 23 files
- authenticate server actions like API routesin 35 of 607, across 7 files
- use next/dynamic for heavy componentsin 34 of 607, across 9 files
- use React.cache for per-request deduplicationin 34 of 607, across 10 files
Said here and by no other author read
- read the core skill first
- use server-minted auth tokens for production
- keep the REST API Key on the server only
- generate a REST API Key if missing
- derive the UID from the authenticated session
- register push tokens after user login resolves
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.