@ttt-productions/ttt-core 0.79.27 → 0.79.28

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.
@@ -84,4 +84,13 @@ export declare const FULL_LIMITS: TttLimits;
84
84
  /** The resolved limit set for the deployed mode. Import THIS (or the alias
85
85
  * constants that re-export from it), never CHARTER_/FULL_ directly. */
86
86
  export declare const ACTIVE_LIMITS: TttLimits;
87
+ /**
88
+ * User-facing copy for a reached count cap — the ONE owner of the cap-message
89
+ * shape, beside the limits it describes. Every callable refusing on a
90
+ * mode-varied count cap formats through this. In charter mode the message
91
+ * names Charter Season so the cap reads as the season's limit rather than the
92
+ * platform's permanent shape; full mode stays neutral. `hint` is an optional
93
+ * trailing sentence (e.g. "Delete a file to add another.").
94
+ */
95
+ export declare const countCapReachedMessage: (subject: string, limit: number, noun: string, hint?: string) => string;
87
96
  //# sourceMappingURL=app-mode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-mode.d.ts","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-mode.d.ts","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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"}
@@ -221,4 +221,16 @@ export const FULL_LIMITS = {
221
221
  /** The resolved limit set for the deployed mode. Import THIS (or the alias
222
222
  * constants that re-export from it), never CHARTER_/FULL_ directly. */
223
223
  export const ACTIVE_LIMITS = byMode(CHARTER_LIMITS, FULL_LIMITS);
224
+ /**
225
+ * User-facing copy for a reached count cap — the ONE owner of the cap-message
226
+ * shape, beside the limits it describes. Every callable refusing on a
227
+ * mode-varied count cap formats through this. In charter mode the message
228
+ * names Charter Season so the cap reads as the season's limit rather than the
229
+ * platform's permanent shape; full mode stays neutral. `hint` is an optional
230
+ * trailing sentence (e.g. "Delete a file to add another.").
231
+ */
232
+ export const countCapReachedMessage = (subject, limit, noun, hint) => {
233
+ const base = byMode(`${subject} already has the Charter Season maximum of ${limit} ${noun} — caps expand when Charter Season ends.`, `${subject} already has the maximum of ${limit} ${noun}.`);
234
+ return hint ? `${base} ${hint}` : base;
235
+ };
224
236
  //# sourceMappingURL=app-mode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-mode.js","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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","sourcesContent":["// App mode + the charter/full limit sets — the single published constant that\n// drives the whole app's cost posture. The flip to full-live is: change\n// APP_MODE → publish ttt-core → install in ttt-prod (root + functions) →\n// deploy. There is NO runtime mode flag and NO console toggle.\n// See ttt-prod docs/design/charter-season-and-app-mode.md.\n//\n// Only real cost/abuse drivers vary by mode: media specs (ttt-media-specs.ts),\n// rate-limit values, count caps, batch sizes. Text-length limits and payment\n// min/max are deliberately mode-invariant — with ONE ruled exception (DJ\n// 2026-07-13): chapter body content (maxChapterContentLength), because it\n// bounds threshold-review reading effort per item during charter season.\n\nexport type AppMode = 'charter' | 'full';\n\n/** The deployed mode. Changing this constant (and publishing) IS the flip. */\nexport const APP_MODE: AppMode = 'charter';\n\n/** Pick the active value for a mode-varied limit. */\nexport const byMode = <T>(charter: T, full: T): T =>\n (APP_MODE as AppMode) === 'charter' ? charter : full;\n\n/** Upstash-style sliding-window duration, e.g. '1 h'. */\nexport type RateLimitWindow = `${number} ${'ms' | 's' | 'm' | 'h' | 'd'}`;\n\nexport interface RateLimitValue {\n maxRequests: number;\n window: RateLimitWindow;\n}\n\nexport interface TttLimits {\n mode: AppMode;\n user: {\n maxCraftSkills: number;\n maxOwnedWorkProjects: number;\n maxAssociatedWorkProjects: number;\n };\n workProject: {\n maxGuildSize: number;\n maxFileFolders: number;\n maxWorkFiles: number;\n maxWorkFileStorageBytes: number;\n maxWorkProjectAuditions: number;\n maxCommissionListings: number;\n maxChapters: number;\n /** Chapter BODY length (chars) — the one mode-varied text limit (DJ 2026-07-13). */\n maxChapterContentLength: number;\n maxTuneTracks: number;\n maxTelevisionEpisodes: number;\n };\n /** Conversation Files container caps — PER CONVERSATION (one guild-invite\n * conversation or one admin-support dispatch thread). Both scopes share one cap\n * set; guild chat channels have no Conversation Files. DJ ruling 2026-07-25. */\n conversation: {\n /** Published active Conversation Files allowed in one conversation. */\n maxConversationFiles: number;\n /** Published STORED-OUTPUT bytes allowed in one conversation. */\n maxConversationFileStorageBytes: number;\n };\n hall: {\n maxSubmitBatch: number;\n };\n batches: {\n trendingFeedProcessLimit: number;\n maxFeedbackSubmitters: number;\n };\n /** Values for the backend rate limiters. Prefixes/factory stay in functions. */\n rateLimits: {\n UPLOAD: RateLimitValue;\n ADMIN_UPLOAD: RateLimitValue;\n CONTENT_WRITE: RateLimitValue;\n VOTE: RateLimitValue;\n MODERATION: RateLimitValue;\n SENSITIVE_ACTION: RateLimitValue;\n DISPLAY_NAME_CHECK: RateLimitValue;\n CHAT_MESSAGE: RateLimitValue;\n ADMIN_TASK: RateLimitValue;\n APPEAL_REVIEW: RateLimitValue;\n BAN_ACTION: RateLimitValue;\n SHARE_TRANSFER: RateLimitValue;\n CONTENT_REPORT: RateLimitValue;\n SHORT_LINK_CLICK: RateLimitValue;\n FOLLOW: RateLimitValue;\n MENTION_HISTORY: RateLimitValue;\n MEDIA_GRANT: RateLimitValue;\n CHAT_GRANT: RateLimitValue;\n OPERATOR_STEP_UP: RateLimitValue;\n ADMIN_MESSAGE_READ: RateLimitValue;\n USER_LOOKUP: RateLimitValue;\n NOTIFICATION_BROADCAST: RateLimitValue;\n NOTIFICATION_ARCHIVE: RateLimitValue;\n NOTIFICATION_MARK_SEEN: RateLimitValue;\n FEEDBACK: RateLimitValue;\n SHORT_LINK_CREATE: RateLimitValue;\n INVITE_ACTION: RateLimitValue;\n LIBRARY_SUBMISSION: RateLimitValue;\n CHANNEL_CREATE: RateLimitValue;\n CHECKOUT_CREATE: RateLimitValue;\n };\n}\n\nexport const CHARTER_LIMITS: TttLimits = {\n mode: 'charter',\n user: {\n maxCraftSkills: 8,\n maxOwnedWorkProjects: 3,\n maxAssociatedWorkProjects: 10,\n },\n workProject: {\n maxGuildSize: 25,\n maxFileFolders: 10,\n maxWorkFiles: 100,\n maxWorkFileStorageBytes: 2_684_354_560, // 2.5 GiB\n maxWorkProjectAuditions: 3,\n maxCommissionListings: 3,\n maxChapters: 5,\n // ~5,000 words — a full real chapter, while keeping the human threshold-review\n // reading effort per item bounded during charter season.\n maxChapterContentLength: 30_000,\n maxTuneTracks: 5,\n maxTelevisionEpisodes: 3,\n },\n // Conversation Files are QUOTA'D PER CONVERSATION (DJ ruling 2026-07-25). Counted on\n // STORED OUTPUT bytes (what actually sits in R2 after transcode), never on raw upload\n // bytes. Rationale: a per-file `maxBytes` cap alone bounds one upload but nothing stops\n // unbounded accumulation across a long-running conversation — this is the abuse brake\n // on that, and it is what `startUpload`'s reservation and the publication transfer both\n // check. Charter: 10 files / 500 MiB per conversation.\n conversation: {\n maxConversationFiles: 10,\n maxConversationFileStorageBytes: 524_288_000, // 500 MiB\n },\n hall: { maxSubmitBatch: 10 },\n batches: { trendingFeedProcessLimit: 250, maxFeedbackSubmitters: 25 },\n rateLimits: {\n // Uploads are charged ONCE per upload (in startUpload); the processor-side\n // check is a non-charging guard.\n UPLOAD: { maxRequests: 15, window: '1 h' },\n // A separate HIGHER bucket for uploads performed through the sanctioned admin\n // authority path — never an exemption. Bulk admin setup work (creating 3\n // auditions is ~5 uploads) exhausts the 15/h user bucket; 60/h covers a real\n // bulk admin session while keeping a brake on a compromised admin token and on\n // third-party quota/cost (Vision / Video Intelligence / R2). DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 60, window: '1 h' },\n // General content-write throttle for the non-upload write callables (text posts,\n // craft-skill edits, support-thread starts) that used to charge UPLOAD. Sharing\n // the 15/h charter upload window meant heavy text posting blocked real uploads\n // and vice versa; a separate 30/h bucket keeps each cost driver on its own\n // ceiling. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 30, window: '1 h' },\n VOTE: { maxRequests: 100, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 30/h: one bucket is shared by ~35 callables (register, become-artisan,\n // create-work, invites, trades, appeals, …) — a real first-hour artisan does\n // 10-15 legitimate sensitive actions; 5/h blocked the intended golden path\n // (proven live by the hosted-dev E2E, 2026-07-07). DJ ruling 2026-07-08.\n SENSITIVE_ACTION: { maxRequests: 30, window: '1 h' },\n // 120/h (charter): checkDisplayNameAvailable is enforceAppCheck:true, so\n // reCAPTCHA-Enterprise attestation is the real abuse gate — this bucket is a\n // cost/DoS ceiling, not the security boundary. It is IP-keyed (pre-registration,\n // no uid yet) and the register form fires it debounced-LIVE as you type, so one\n // legit registrant spends ~3-8 checks and shared NAT/CGNAT multiplies that across\n // everyone on the IP. The old 20/h false-rejected legitimate shared-IP users and\n // could not fit a full hosted-dev E2E suite run on one IP. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 120, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 125, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 5, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 20, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // Scoped-media grant minting (createMediaGrant). Its OWN bucket — it used to charge\n // MENTION_HISTORY, an unrelated surface, so neither limit meant what its name said and the\n // two shared a budget sized for neither (BACKEND-013). Refresh-driven, not user-initiated:\n // a grant is short-TTL (60 min work media, 15 min chat/folder scopes) and the client re-mints\n // under expiry, so a browsing session naturally spends several per hour PER SCOPE (each work,\n // each private file folder, each conversation). Cheap to serve (a Firestore permission read\n // plus an HMAC sign — no third-party cost), and a false 429 breaks media rendering outright,\n // so the ceiling is generous: it exists to stop a scripted grant-farming sweep, not to pace a\n // real reader. 120/h charter.\n MEDIA_GRANT: { maxRequests: 120, window: '1 h' },\n // Realtime chat grant minting (mintChatGrant) — previously NO limiter at all (BACKEND-013),\n // despite minting signed grants at concurrency: 20. Same refresh-driven shape as MEDIA_GRANT:\n // a 15-minute grant TTL means ~4 mints/hour for the inbox plus ~4 per open channel, doubled\n // again by socket reconnects on a flaky mobile network. It stays behind the per-channel\n // authorizeChannelScope gate, so this bucket is a cost/DoS ceiling rather than the\n // authorization boundary — sized to match MEDIA_GRANT so the two grant surfaces are paced\n // alike and neither can be starved by the other (separate Redis prefixes).\n CHAT_GRANT: { maxRequests: 120, window: '1 h' },\n // Operator step-up TOTP verification (confirmOperatorStepUp / verifyOperatorStepUp) —\n // previously unthrottled, so an attacker holding a stolen full-admin session could\n // brute-force the 6-digit second factor guarding the safety console (BACKEND-013).\n // DELIBERATELY THE TIGHTEST BUCKET IN THE SET: a real operator enrolls once and verifies\n // roughly twice an hour (the grant window is 30 minutes), so 10/h absorbs every legitimate\n // mistype while capping guessing at 10 attempts per hour against a code that rotates every\n // 30 seconds. Mode-INVARIANT — this is an admin security control, not a cost driver, so it\n // never loosens at the full-mode flip (same posture as MODERATION / BAN_ACTION / USER_LOOKUP).\n // The lockout-after-N-failures behaviour is a separate app-side concern; this is the throttle.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup (searchPublicUsers / lookupUserByEmailOrUid) — a read, so a\n // generous per-minute bucket that still backstops a scripted enumeration sweep. Admin-only\n // and not a cost driver, so the value is the same in both modes (like MODERATION/ADMIN_TASK).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 30, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 10, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 5, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 5, window: '1 h' },\n },\n};\n\nexport const FULL_LIMITS: TttLimits = {\n mode: 'full',\n user: {\n maxCraftSkills: 24,\n maxOwnedWorkProjects: 25,\n maxAssociatedWorkProjects: 100,\n },\n workProject: {\n maxGuildSize: 250,\n maxFileFolders: 25,\n maxWorkFiles: 1000,\n maxWorkFileStorageBytes: 26_843_545_600, // 25 GiB\n maxWorkProjectAuditions: 10,\n maxCommissionListings: 10,\n maxChapters: 50,\n // The intentional design ceiling (~17,000 words; trivially inside Firestore's\n // 1 MiB doc limit) — the audit trail deliberately stores field LENGTHS, not\n // content, because chapters can be this large (runUpdateChapterDetails).\n maxChapterContentLength: 100_000,\n maxTuneTracks: 50,\n maxTelevisionEpisodes: 25,\n },\n // 20 files / 1 GiB (full): the same per-conversation Conversation Files quota as\n // charter, scaled for a public-launch conversation. Still a hard ceiling on\n // unbounded R2 accumulation. See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n conversation: {\n maxConversationFiles: 20,\n maxConversationFileStorageBytes: 1_073_741_824, // 1 GiB\n },\n hall: { maxSubmitBatch: 50 },\n batches: { trendingFeedProcessLimit: 1000, maxFeedbackSubmitters: 100 },\n rateLimits: {\n UPLOAD: { maxRequests: 30, window: '1 h' },\n // 120/h (full): the same sanctioned-admin-path upload bucket as charter, scaled\n // with UPLOAD — always HIGHER than the user bucket, never an exemption, so a\n // compromised admin token and third-party media cost stay bounded. See the\n // CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 120, window: '1 h' },\n // 60/h (full): the same non-upload content-write bucket as charter, scaled with\n // UPLOAD so text writes and media uploads never consume each other's ceiling.\n // See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 60, window: '1 h' },\n VOTE: { maxRequests: 200, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 60/h: full-mode power users managing several works do dozens of legitimate\n // management actions per hour; still a hard backstop against scripts.\n // DJ ruling 2026-07-08 (charter 30 / full 60, single bucket; a bucket split\n // is filed post-launch in ttt-prod docs/post-launch/).\n SENSITIVE_ACTION: { maxRequests: 60, window: '1 h' },\n // 300/h (full): same reasoning as the charter value above (App-Check-gated\n // cost ceiling, IP-keyed, debounced-live). Public-launch CGNAT exposure is far\n // larger — hundreds of concurrent legit registrants can sit behind one carrier\n // IP — so err generous; the only cost of \"too high\" is trivial Firestore reads,\n // while \"too low\" false-rejects real users. Raise on evidence (watch prod logs\n // for legit 429s) rather than pre-emptively tightening. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 300, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 150, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 20, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 60, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // 240/h (full): the same grant-minting buckets as charter, doubled with the surrounding\n // content limits. Full mode raises the ceilings a grant is minted PER (250-guildmate guilds,\n // 25 file folders, 1000 work files, 10 auditions), so one session legitimately touches more\n // scopes and re-mints more often. See the CHARTER_LIMITS notes above.\n MEDIA_GRANT: { maxRequests: 240, window: '1 h' },\n CHAT_GRANT: { maxRequests: 240, window: '1 h' },\n // Operator step-up stays at 10/h in BOTH modes — an admin second-factor throttle is a\n // security control, not a cost lever, so it never loosens at the flip. See CHARTER_LIMITS.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup — same read bucket in both modes (see CHARTER_LIMITS note).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 60, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 20, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 20, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 20, window: '1 h' },\n },\n};\n\n/** The resolved limit set for the deployed mode. Import THIS (or the alias\n * constants that re-export from it), never CHARTER_/FULL_ directly. */\nexport const ACTIVE_LIMITS: TttLimits = byMode(CHARTER_LIMITS, FULL_LIMITS);\n"]}
1
+ {"version":3,"file":"app-mode.js","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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","sourcesContent":["// App mode + the charter/full limit sets — the single published constant that\n// drives the whole app's cost posture. The flip to full-live is: change\n// APP_MODE → publish ttt-core → install in ttt-prod (root + functions) →\n// deploy. There is NO runtime mode flag and NO console toggle.\n// See ttt-prod docs/design/charter-season-and-app-mode.md.\n//\n// Only real cost/abuse drivers vary by mode: media specs (ttt-media-specs.ts),\n// rate-limit values, count caps, batch sizes. Text-length limits and payment\n// min/max are deliberately mode-invariant — with ONE ruled exception (DJ\n// 2026-07-13): chapter body content (maxChapterContentLength), because it\n// bounds threshold-review reading effort per item during charter season.\n\nexport type AppMode = 'charter' | 'full';\n\n/** The deployed mode. Changing this constant (and publishing) IS the flip. */\nexport const APP_MODE: AppMode = 'charter';\n\n/** Pick the active value for a mode-varied limit. */\nexport const byMode = <T>(charter: T, full: T): T =>\n (APP_MODE as AppMode) === 'charter' ? charter : full;\n\n/** Upstash-style sliding-window duration, e.g. '1 h'. */\nexport type RateLimitWindow = `${number} ${'ms' | 's' | 'm' | 'h' | 'd'}`;\n\nexport interface RateLimitValue {\n maxRequests: number;\n window: RateLimitWindow;\n}\n\nexport interface TttLimits {\n mode: AppMode;\n user: {\n maxCraftSkills: number;\n maxOwnedWorkProjects: number;\n maxAssociatedWorkProjects: number;\n };\n workProject: {\n maxGuildSize: number;\n maxFileFolders: number;\n maxWorkFiles: number;\n maxWorkFileStorageBytes: number;\n maxWorkProjectAuditions: number;\n maxCommissionListings: number;\n maxChapters: number;\n /** Chapter BODY length (chars) — the one mode-varied text limit (DJ 2026-07-13). */\n maxChapterContentLength: number;\n maxTuneTracks: number;\n maxTelevisionEpisodes: number;\n };\n /** Conversation Files container caps — PER CONVERSATION (one guild-invite\n * conversation or one admin-support dispatch thread). Both scopes share one cap\n * set; guild chat channels have no Conversation Files. DJ ruling 2026-07-25. */\n conversation: {\n /** Published active Conversation Files allowed in one conversation. */\n maxConversationFiles: number;\n /** Published STORED-OUTPUT bytes allowed in one conversation. */\n maxConversationFileStorageBytes: number;\n };\n hall: {\n maxSubmitBatch: number;\n };\n batches: {\n trendingFeedProcessLimit: number;\n maxFeedbackSubmitters: number;\n };\n /** Values for the backend rate limiters. Prefixes/factory stay in functions. */\n rateLimits: {\n UPLOAD: RateLimitValue;\n ADMIN_UPLOAD: RateLimitValue;\n CONTENT_WRITE: RateLimitValue;\n VOTE: RateLimitValue;\n MODERATION: RateLimitValue;\n SENSITIVE_ACTION: RateLimitValue;\n DISPLAY_NAME_CHECK: RateLimitValue;\n CHAT_MESSAGE: RateLimitValue;\n ADMIN_TASK: RateLimitValue;\n APPEAL_REVIEW: RateLimitValue;\n BAN_ACTION: RateLimitValue;\n SHARE_TRANSFER: RateLimitValue;\n CONTENT_REPORT: RateLimitValue;\n SHORT_LINK_CLICK: RateLimitValue;\n FOLLOW: RateLimitValue;\n MENTION_HISTORY: RateLimitValue;\n MEDIA_GRANT: RateLimitValue;\n CHAT_GRANT: RateLimitValue;\n OPERATOR_STEP_UP: RateLimitValue;\n ADMIN_MESSAGE_READ: RateLimitValue;\n USER_LOOKUP: RateLimitValue;\n NOTIFICATION_BROADCAST: RateLimitValue;\n NOTIFICATION_ARCHIVE: RateLimitValue;\n NOTIFICATION_MARK_SEEN: RateLimitValue;\n FEEDBACK: RateLimitValue;\n SHORT_LINK_CREATE: RateLimitValue;\n INVITE_ACTION: RateLimitValue;\n LIBRARY_SUBMISSION: RateLimitValue;\n CHANNEL_CREATE: RateLimitValue;\n CHECKOUT_CREATE: RateLimitValue;\n };\n}\n\nexport const CHARTER_LIMITS: TttLimits = {\n mode: 'charter',\n user: {\n maxCraftSkills: 8,\n maxOwnedWorkProjects: 3,\n maxAssociatedWorkProjects: 10,\n },\n workProject: {\n maxGuildSize: 25,\n maxFileFolders: 10,\n maxWorkFiles: 100,\n maxWorkFileStorageBytes: 2_684_354_560, // 2.5 GiB\n maxWorkProjectAuditions: 3,\n maxCommissionListings: 3,\n maxChapters: 5,\n // ~5,000 words — a full real chapter, while keeping the human threshold-review\n // reading effort per item bounded during charter season.\n maxChapterContentLength: 30_000,\n maxTuneTracks: 5,\n maxTelevisionEpisodes: 3,\n },\n // Conversation Files are QUOTA'D PER CONVERSATION (DJ ruling 2026-07-25). Counted on\n // STORED OUTPUT bytes (what actually sits in R2 after transcode), never on raw upload\n // bytes. Rationale: a per-file `maxBytes` cap alone bounds one upload but nothing stops\n // unbounded accumulation across a long-running conversation — this is the abuse brake\n // on that, and it is what `startUpload`'s reservation and the publication transfer both\n // check. Charter: 10 files / 500 MiB per conversation.\n conversation: {\n maxConversationFiles: 10,\n maxConversationFileStorageBytes: 524_288_000, // 500 MiB\n },\n hall: { maxSubmitBatch: 10 },\n batches: { trendingFeedProcessLimit: 250, maxFeedbackSubmitters: 25 },\n rateLimits: {\n // Uploads are charged ONCE per upload (in startUpload); the processor-side\n // check is a non-charging guard.\n UPLOAD: { maxRequests: 15, window: '1 h' },\n // A separate HIGHER bucket for uploads performed through the sanctioned admin\n // authority path — never an exemption. Bulk admin setup work (creating 3\n // auditions is ~5 uploads) exhausts the 15/h user bucket; 60/h covers a real\n // bulk admin session while keeping a brake on a compromised admin token and on\n // third-party quota/cost (Vision / Video Intelligence / R2). DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 60, window: '1 h' },\n // General content-write throttle for the non-upload write callables (text posts,\n // craft-skill edits, support-thread starts) that used to charge UPLOAD. Sharing\n // the 15/h charter upload window meant heavy text posting blocked real uploads\n // and vice versa; a separate 30/h bucket keeps each cost driver on its own\n // ceiling. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 30, window: '1 h' },\n VOTE: { maxRequests: 100, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 30/h: one bucket is shared by ~35 callables (register, become-artisan,\n // create-work, invites, trades, appeals, …) — a real first-hour artisan does\n // 10-15 legitimate sensitive actions; 5/h blocked the intended golden path\n // (proven live by the hosted-dev E2E, 2026-07-07). DJ ruling 2026-07-08.\n SENSITIVE_ACTION: { maxRequests: 30, window: '1 h' },\n // 120/h (charter): checkDisplayNameAvailable is enforceAppCheck:true, so\n // reCAPTCHA-Enterprise attestation is the real abuse gate — this bucket is a\n // cost/DoS ceiling, not the security boundary. It is IP-keyed (pre-registration,\n // no uid yet) and the register form fires it debounced-LIVE as you type, so one\n // legit registrant spends ~3-8 checks and shared NAT/CGNAT multiplies that across\n // everyone on the IP. The old 20/h false-rejected legitimate shared-IP users and\n // could not fit a full hosted-dev E2E suite run on one IP. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 120, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 125, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 5, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 20, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // Scoped-media grant minting (createMediaGrant). Its OWN bucket — it used to charge\n // MENTION_HISTORY, an unrelated surface, so neither limit meant what its name said and the\n // two shared a budget sized for neither (BACKEND-013). Refresh-driven, not user-initiated:\n // a grant is short-TTL (60 min work media, 15 min chat/folder scopes) and the client re-mints\n // under expiry, so a browsing session naturally spends several per hour PER SCOPE (each work,\n // each private file folder, each conversation). Cheap to serve (a Firestore permission read\n // plus an HMAC sign — no third-party cost), and a false 429 breaks media rendering outright,\n // so the ceiling is generous: it exists to stop a scripted grant-farming sweep, not to pace a\n // real reader. 120/h charter.\n MEDIA_GRANT: { maxRequests: 120, window: '1 h' },\n // Realtime chat grant minting (mintChatGrant) — previously NO limiter at all (BACKEND-013),\n // despite minting signed grants at concurrency: 20. Same refresh-driven shape as MEDIA_GRANT:\n // a 15-minute grant TTL means ~4 mints/hour for the inbox plus ~4 per open channel, doubled\n // again by socket reconnects on a flaky mobile network. It stays behind the per-channel\n // authorizeChannelScope gate, so this bucket is a cost/DoS ceiling rather than the\n // authorization boundary — sized to match MEDIA_GRANT so the two grant surfaces are paced\n // alike and neither can be starved by the other (separate Redis prefixes).\n CHAT_GRANT: { maxRequests: 120, window: '1 h' },\n // Operator step-up TOTP verification (confirmOperatorStepUp / verifyOperatorStepUp) —\n // previously unthrottled, so an attacker holding a stolen full-admin session could\n // brute-force the 6-digit second factor guarding the safety console (BACKEND-013).\n // DELIBERATELY THE TIGHTEST BUCKET IN THE SET: a real operator enrolls once and verifies\n // roughly twice an hour (the grant window is 30 minutes), so 10/h absorbs every legitimate\n // mistype while capping guessing at 10 attempts per hour against a code that rotates every\n // 30 seconds. Mode-INVARIANT — this is an admin security control, not a cost driver, so it\n // never loosens at the full-mode flip (same posture as MODERATION / BAN_ACTION / USER_LOOKUP).\n // The lockout-after-N-failures behaviour is a separate app-side concern; this is the throttle.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup (searchPublicUsers / lookupUserByEmailOrUid) — a read, so a\n // generous per-minute bucket that still backstops a scripted enumeration sweep. Admin-only\n // and not a cost driver, so the value is the same in both modes (like MODERATION/ADMIN_TASK).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 30, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 10, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 5, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 5, window: '1 h' },\n },\n};\n\nexport const FULL_LIMITS: TttLimits = {\n mode: 'full',\n user: {\n maxCraftSkills: 24,\n maxOwnedWorkProjects: 25,\n maxAssociatedWorkProjects: 100,\n },\n workProject: {\n maxGuildSize: 250,\n maxFileFolders: 25,\n maxWorkFiles: 1000,\n maxWorkFileStorageBytes: 26_843_545_600, // 25 GiB\n maxWorkProjectAuditions: 10,\n maxCommissionListings: 10,\n maxChapters: 50,\n // The intentional design ceiling (~17,000 words; trivially inside Firestore's\n // 1 MiB doc limit) — the audit trail deliberately stores field LENGTHS, not\n // content, because chapters can be this large (runUpdateChapterDetails).\n maxChapterContentLength: 100_000,\n maxTuneTracks: 50,\n maxTelevisionEpisodes: 25,\n },\n // 20 files / 1 GiB (full): the same per-conversation Conversation Files quota as\n // charter, scaled for a public-launch conversation. Still a hard ceiling on\n // unbounded R2 accumulation. See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n conversation: {\n maxConversationFiles: 20,\n maxConversationFileStorageBytes: 1_073_741_824, // 1 GiB\n },\n hall: { maxSubmitBatch: 50 },\n batches: { trendingFeedProcessLimit: 1000, maxFeedbackSubmitters: 100 },\n rateLimits: {\n UPLOAD: { maxRequests: 30, window: '1 h' },\n // 120/h (full): the same sanctioned-admin-path upload bucket as charter, scaled\n // with UPLOAD — always HIGHER than the user bucket, never an exemption, so a\n // compromised admin token and third-party media cost stay bounded. See the\n // CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 120, window: '1 h' },\n // 60/h (full): the same non-upload content-write bucket as charter, scaled with\n // UPLOAD so text writes and media uploads never consume each other's ceiling.\n // See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 60, window: '1 h' },\n VOTE: { maxRequests: 200, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 60/h: full-mode power users managing several works do dozens of legitimate\n // management actions per hour; still a hard backstop against scripts.\n // DJ ruling 2026-07-08 (charter 30 / full 60, single bucket; a bucket split\n // is filed post-launch in ttt-prod docs/post-launch/).\n SENSITIVE_ACTION: { maxRequests: 60, window: '1 h' },\n // 300/h (full): same reasoning as the charter value above (App-Check-gated\n // cost ceiling, IP-keyed, debounced-live). Public-launch CGNAT exposure is far\n // larger — hundreds of concurrent legit registrants can sit behind one carrier\n // IP — so err generous; the only cost of \"too high\" is trivial Firestore reads,\n // while \"too low\" false-rejects real users. Raise on evidence (watch prod logs\n // for legit 429s) rather than pre-emptively tightening. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 300, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 150, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 20, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 60, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // 240/h (full): the same grant-minting buckets as charter, doubled with the surrounding\n // content limits. Full mode raises the ceilings a grant is minted PER (250-guildmate guilds,\n // 25 file folders, 1000 work files, 10 auditions), so one session legitimately touches more\n // scopes and re-mints more often. See the CHARTER_LIMITS notes above.\n MEDIA_GRANT: { maxRequests: 240, window: '1 h' },\n CHAT_GRANT: { maxRequests: 240, window: '1 h' },\n // Operator step-up stays at 10/h in BOTH modes — an admin second-factor throttle is a\n // security control, not a cost lever, so it never loosens at the flip. See CHARTER_LIMITS.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup — same read bucket in both modes (see CHARTER_LIMITS note).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 60, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 20, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 20, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 20, window: '1 h' },\n },\n};\n\n/** The resolved limit set for the deployed mode. Import THIS (or the alias\n * constants that re-export from it), never CHARTER_/FULL_ directly. */\nexport const ACTIVE_LIMITS: TttLimits = byMode(CHARTER_LIMITS, FULL_LIMITS);\n\n/**\n * User-facing copy for a reached count cap — the ONE owner of the cap-message\n * shape, beside the limits it describes. Every callable refusing on a\n * mode-varied count cap formats through this. In charter mode the message\n * names Charter Season so the cap reads as the season's limit rather than the\n * platform's permanent shape; full mode stays neutral. `hint` is an optional\n * trailing sentence (e.g. \"Delete a file to add another.\").\n */\nexport const countCapReachedMessage = (\n subject: string,\n limit: number,\n noun: string,\n hint?: string,\n): string => {\n const base = byMode(\n `${subject} already has the Charter Season maximum of ${limit} ${noun} — caps expand when Charter Season ends.`,\n `${subject} already has the maximum of ${limit} ${noun}.`,\n );\n return hint ? `${base} ${hint}` : base;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/ttt-core",
3
- "version": "0.79.27",
3
+ "version": "0.79.28",
4
4
  "description": "Core types, Firestore path constants, and shared constants for TTT Productions",
5
5
  "repository": {
6
6
  "type": "git",