@kavtuai/guildgate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +21 -0
  3. package/README.md +399 -0
  4. package/README.tr.md +397 -0
  5. package/SECURITY.md +36 -0
  6. package/TEST_REPORT.md +50 -0
  7. package/bin/guildgate-doctor.mjs +91 -0
  8. package/bin/guildgate-writing-check.mjs +73 -0
  9. package/dist/action.d.ts +65 -0
  10. package/dist/action.d.ts.map +1 -0
  11. package/dist/action.js +4 -0
  12. package/dist/action.js.map +1 -0
  13. package/dist/cache.d.ts +23 -0
  14. package/dist/cache.d.ts.map +1 -0
  15. package/dist/cache.js +53 -0
  16. package/dist/cache.js.map +1 -0
  17. package/dist/crypto.d.ts +15 -0
  18. package/dist/crypto.d.ts.map +1 -0
  19. package/dist/crypto.js +87 -0
  20. package/dist/crypto.js.map +1 -0
  21. package/dist/discord/authorizer.d.ts +41 -0
  22. package/dist/discord/authorizer.d.ts.map +1 -0
  23. package/dist/discord/authorizer.js +101 -0
  24. package/dist/discord/authorizer.js.map +1 -0
  25. package/dist/discord/oauth.d.ts +64 -0
  26. package/dist/discord/oauth.d.ts.map +1 -0
  27. package/dist/discord/oauth.js +226 -0
  28. package/dist/discord/oauth.js.map +1 -0
  29. package/dist/discord/permissions.d.ts +85 -0
  30. package/dist/discord/permissions.d.ts.map +1 -0
  31. package/dist/discord/permissions.js +99 -0
  32. package/dist/discord/permissions.js.map +1 -0
  33. package/dist/discord.d.ts +4 -0
  34. package/dist/discord.d.ts.map +1 -0
  35. package/dist/discord.js +4 -0
  36. package/dist/discord.js.map +1 -0
  37. package/dist/errors.d.ts +43 -0
  38. package/dist/errors.d.ts.map +1 -0
  39. package/dist/errors.js +57 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/express.d.ts +3 -0
  42. package/dist/express.d.ts.map +1 -0
  43. package/dist/express.js +3 -0
  44. package/dist/express.js.map +1 -0
  45. package/dist/fastify.d.ts +3 -0
  46. package/dist/fastify.d.ts.map +1 -0
  47. package/dist/fastify.js +3 -0
  48. package/dist/fastify.js.map +1 -0
  49. package/dist/http/express.d.ts +24 -0
  50. package/dist/http/express.d.ts.map +1 -0
  51. package/dist/http/express.js +60 -0
  52. package/dist/http/express.js.map +1 -0
  53. package/dist/http/fastify.d.ts +25 -0
  54. package/dist/http/fastify.d.ts.map +1 -0
  55. package/dist/http/fastify.js +57 -0
  56. package/dist/http/fastify.js.map +1 -0
  57. package/dist/i18n.d.ts +5 -0
  58. package/dist/i18n.d.ts.map +1 -0
  59. package/dist/i18n.js +66 -0
  60. package/dist/i18n.js.map +1 -0
  61. package/dist/index.d.ts +13 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +13 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/kernel.d.ts +83 -0
  66. package/dist/kernel.d.ts.map +1 -0
  67. package/dist/kernel.js +395 -0
  68. package/dist/kernel.js.map +1 -0
  69. package/dist/memory-store.d.ts +13 -0
  70. package/dist/memory-store.d.ts.map +1 -0
  71. package/dist/memory-store.js +261 -0
  72. package/dist/memory-store.js.map +1 -0
  73. package/dist/realtime/hub.d.ts +42 -0
  74. package/dist/realtime/hub.d.ts.map +1 -0
  75. package/dist/realtime/hub.js +152 -0
  76. package/dist/realtime/hub.js.map +1 -0
  77. package/dist/realtime/outbox.d.ts +12 -0
  78. package/dist/realtime/outbox.d.ts.map +1 -0
  79. package/dist/realtime/outbox.js +18 -0
  80. package/dist/realtime/outbox.js.map +1 -0
  81. package/dist/realtime.d.ts +3 -0
  82. package/dist/realtime.d.ts.map +1 -0
  83. package/dist/realtime.js +3 -0
  84. package/dist/realtime.js.map +1 -0
  85. package/dist/redis.d.ts +49 -0
  86. package/dist/redis.d.ts.map +1 -0
  87. package/dist/redis.js +233 -0
  88. package/dist/redis.js.map +1 -0
  89. package/dist/resilience.d.ts +23 -0
  90. package/dist/resilience.d.ts.map +1 -0
  91. package/dist/resilience.js +85 -0
  92. package/dist/resilience.js.map +1 -0
  93. package/dist/security.d.ts +21 -0
  94. package/dist/security.d.ts.map +1 -0
  95. package/dist/security.js +94 -0
  96. package/dist/security.js.map +1 -0
  97. package/dist/session.d.ts +32 -0
  98. package/dist/session.d.ts.map +1 -0
  99. package/dist/session.js +97 -0
  100. package/dist/session.js.map +1 -0
  101. package/dist/stores.d.ts +73 -0
  102. package/dist/stores.d.ts.map +1 -0
  103. package/dist/stores.js +4 -0
  104. package/dist/stores.js.map +1 -0
  105. package/dist/testing.d.ts +32 -0
  106. package/dist/testing.d.ts.map +1 -0
  107. package/dist/testing.js +60 -0
  108. package/dist/testing.js.map +1 -0
  109. package/dist/types.d.ts +183 -0
  110. package/dist/types.d.ts.map +1 -0
  111. package/dist/types.js +4 -0
  112. package/dist/types.js.map +1 -0
  113. package/docs/WRITING_STYLE.md +118 -0
  114. package/docs/architecture.md +83 -0
  115. package/docs/custom-stores.md +111 -0
  116. package/docs/owner-management.md +44 -0
  117. package/docs/research-notes.md +61 -0
  118. package/docs/setup.md +104 -0
  119. package/docs/threat-model.md +79 -0
  120. package/docs/tr/arastirma-notlari.md +41 -0
  121. package/docs/tr/guvenlik-bildirimi.md +7 -0
  122. package/docs/tr/kurulum.md +63 -0
  123. package/docs/tr/mimari.md +59 -0
  124. package/docs/tr/sahip-yonetimi.md +7 -0
  125. package/docs/tr/tehdit-modeli.md +39 -0
  126. package/docs/tr/veritabani-suruculeri.md +23 -0
  127. package/docs/tr/yazim-kilavuzu.md +116 -0
  128. package/docs/uml/action-sequence.mmd +39 -0
  129. package/docs/uml/components.mmd +84 -0
  130. package/docs/uml/deployment.mmd +19 -0
  131. package/docs/uml/oauth-sequence.mmd +30 -0
  132. package/docs/uml/realtime-sequence.mmd +28 -0
  133. package/package.json +90 -0
@@ -0,0 +1,73 @@
1
+ import type { AuditEvent, BlockRecord, CacheEntry, IdempotencyBeginResult, IdempotencyRecord, MaintenanceState, OAuthCredentialRecord, OAuthStateRecord, OutboxRecord, RateLimitHitInput, RateLimitHitResult, SessionRecord } from "./types.js";
2
+ export interface SessionStore {
3
+ get(idHash: string): Promise<SessionRecord | null>;
4
+ set(record: SessionRecord): Promise<void>;
5
+ delete(idHash: string): Promise<void>;
6
+ listByUser(userId: string): Promise<SessionRecord[]>;
7
+ }
8
+ export interface OAuthStateStore {
9
+ put(record: OAuthStateRecord): Promise<void>;
10
+ consume(stateHash: string, nowIso: string): Promise<OAuthStateRecord | null>;
11
+ }
12
+ export interface OAuthCredentialStore {
13
+ get(provider: string, userId: string): Promise<OAuthCredentialRecord | null>;
14
+ set(record: OAuthCredentialRecord): Promise<void>;
15
+ delete(provider: string, userId: string): Promise<void>;
16
+ }
17
+ export interface RateLimitStore {
18
+ hit(input: RateLimitHitInput): Promise<RateLimitHitResult>;
19
+ reset(key: string): Promise<void>;
20
+ }
21
+ export interface CacheStore {
22
+ get<T>(key: string): Promise<CacheEntry<T> | null>;
23
+ set<T>(key: string, entry: CacheEntry<T>): Promise<void>;
24
+ delete(key: string): Promise<void>;
25
+ deleteByTags(tags: string[]): Promise<number>;
26
+ }
27
+ export interface IdempotencyStore {
28
+ begin(record: IdempotencyRecord): Promise<IdempotencyBeginResult>;
29
+ complete(key: string, response: unknown, expiresAtMs: number): Promise<void>;
30
+ fail(key: string): Promise<void>;
31
+ get(key: string): Promise<IdempotencyRecord | null>;
32
+ }
33
+ export interface LockStore {
34
+ acquire(key: string, token: string, ttlMs: number, waitMs: number): Promise<boolean>;
35
+ release(key: string, token: string): Promise<void>;
36
+ }
37
+ export interface AuditStore {
38
+ write(event: AuditEvent): Promise<void>;
39
+ list?(filter?: {
40
+ userId?: string;
41
+ action?: string;
42
+ limit?: number;
43
+ }): Promise<AuditEvent[]>;
44
+ }
45
+ export interface OutboxStore {
46
+ enqueue(record: OutboxRecord): Promise<void>;
47
+ next(limit: number): Promise<OutboxRecord[]>;
48
+ markPublished(id: string, publishedAt: string): Promise<void>;
49
+ markFailed(id: string, error: string): Promise<void>;
50
+ }
51
+ export interface PolicyStore {
52
+ getMaintenance(): Promise<MaintenanceState>;
53
+ setMaintenance(state: MaintenanceState): Promise<void>;
54
+ getBlock(subjectType: BlockRecord["subjectType"], subjectId: string, nowIso: string): Promise<BlockRecord | null>;
55
+ putBlock(record: BlockRecord): Promise<void>;
56
+ removeBlock(subjectType: BlockRecord["subjectType"], subjectId: string): Promise<void>;
57
+ getPolicyVersion(): Promise<number>;
58
+ bumpPolicyVersion(): Promise<number>;
59
+ }
60
+ export interface GuildGateStores {
61
+ sessions: SessionStore;
62
+ oauthStates: OAuthStateStore;
63
+ credentials: OAuthCredentialStore;
64
+ rateLimits: RateLimitStore;
65
+ cache: CacheStore;
66
+ idempotency: IdempotencyStore;
67
+ locks: LockStore;
68
+ audit: AuditStore;
69
+ outbox: OutboxStore;
70
+ policies: PolicyStore;
71
+ }
72
+ export declare function composeStores(base: GuildGateStores, overrides: Partial<GuildGateStores>): GuildGateStores;
73
+ //# sourceMappingURL=stores.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"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"}
package/dist/stores.js ADDED
@@ -0,0 +1,4 @@
1
+ export function composeStores(base, overrides) {
2
+ return { ...base, ...overrides };
3
+ }
4
+ //# sourceMappingURL=stores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stores.js","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AA4FA,MAAM,UAAU,aAAa,CAAC,IAAqB,EAAE,SAAmC;IACtF,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type GuildGateConfig } from "./kernel.js";
2
+ import type { SupportedLocale } from "./types.js";
3
+ export declare function createGuildGateTestHarness(overrides?: Partial<GuildGateConfig>): {
4
+ kernel: import("./kernel.js").GuildGateKernel;
5
+ stores: import("./stores.js").GuildGateStores;
6
+ login(userId?: string, locale?: SupportedLocale): Promise<{
7
+ session: import("./types.js").SessionRecord;
8
+ sessionToken: string;
9
+ csrfToken: string;
10
+ setCookie: string;
11
+ }>;
12
+ request(input: {
13
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
14
+ body?: unknown;
15
+ sessionToken?: string;
16
+ csrfToken?: string;
17
+ idempotencyKey?: string;
18
+ locale?: SupportedLocale;
19
+ }): {
20
+ readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
21
+ readonly path: "/test";
22
+ readonly input: unknown;
23
+ readonly origin: "http://localhost:3000";
24
+ readonly ip: "127.0.0.1";
25
+ readonly userAgent: "guildgate-test";
26
+ readonly sessionToken: string | undefined;
27
+ readonly csrfToken: string | undefined;
28
+ readonly idempotencyKey: string | undefined;
29
+ readonly locale: SupportedLocale;
30
+ };
31
+ };
32
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAgB,0BAA0B,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;;;oCAuCpC,eAAe;;;;;;mBAGvC;QACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACrD,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,eAAe,CAAC;KAC1B;;;;;;;;;;;;EAeJ"}
@@ -0,0 +1,60 @@
1
+ import { createMemoryStoreBundle } from "./memory-store.js";
2
+ import { createGuildGate } from "./kernel.js";
3
+ export function createGuildGateTestHarness(overrides) {
4
+ const stores = createMemoryStoreBundle();
5
+ const defaultSecurity = {
6
+ allowedOrigins: ["http://localhost:3000"],
7
+ csrfSecret: "test-csrf-secret-that-is-long-enough-123456",
8
+ auditIpSalt: "test-audit-salt-123456",
9
+ session: {
10
+ ttlMs: 60 * 60_000,
11
+ idleTimeoutMs: 30 * 60_000,
12
+ rotateAfterMs: 15 * 60_000,
13
+ maximumSessionsPerUser: 5,
14
+ },
15
+ };
16
+ const config = {
17
+ app: {
18
+ name: overrides?.app?.name ?? "GuildGate Test",
19
+ environment: overrides?.app?.environment ?? "test",
20
+ baseUrl: overrides?.app?.baseUrl ?? "http://localhost:3000",
21
+ },
22
+ owners: overrides?.owners ?? ["owner-1"],
23
+ locale: overrides?.locale ?? { default: "en" },
24
+ security: {
25
+ ...defaultSecurity,
26
+ ...overrides?.security,
27
+ session: {
28
+ ...defaultSecurity.session,
29
+ ...overrides?.security?.session,
30
+ },
31
+ },
32
+ stores: overrides?.stores ?? stores,
33
+ realtime: overrides?.realtime,
34
+ audit: overrides?.audit,
35
+ clock: overrides?.clock,
36
+ };
37
+ const kernel = createGuildGate(config);
38
+ return {
39
+ kernel,
40
+ stores: config.stores,
41
+ async login(userId = "user-1", locale = "en") {
42
+ return kernel.createSession({ userId, locale });
43
+ },
44
+ request(input) {
45
+ return {
46
+ method: input.method ?? "POST",
47
+ path: "/test",
48
+ input: input.body,
49
+ origin: "http://localhost:3000",
50
+ ip: "127.0.0.1",
51
+ userAgent: "guildgate-test",
52
+ sessionToken: input.sessionToken,
53
+ csrfToken: input.csrfToken,
54
+ idempotencyKey: input.idempotencyKey,
55
+ locale: input.locale ?? "en",
56
+ };
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAwB,MAAM,aAAa,CAAC;AAGpE,MAAM,UAAU,0BAA0B,CAAC,SAAoC;IAC7E,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,MAAM,eAAe,GAAgC;QACnD,cAAc,EAAE,CAAC,uBAAuB,CAAC;QACzC,UAAU,EAAE,6CAA6C;QACzD,WAAW,EAAE,wBAAwB;QACrC,OAAO,EAAE;YACP,KAAK,EAAE,EAAE,GAAG,MAAM;YAClB,aAAa,EAAE,EAAE,GAAG,MAAM;YAC1B,aAAa,EAAE,EAAE,GAAG,MAAM;YAC1B,sBAAsB,EAAE,CAAC;SAC1B;KACF,CAAC;IACF,MAAM,MAAM,GAAoB;QAC9B,GAAG,EAAE;YACH,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,IAAI,gBAAgB;YAC9C,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,IAAI,MAAM;YAClD,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,IAAI,uBAAuB;SAC5D;QACD,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC;QACxC,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;QAC9C,QAAQ,EAAE;YACR,GAAG,eAAe;YAClB,GAAG,SAAS,EAAE,QAAQ;YACtB,OAAO,EAAE;gBACP,GAAG,eAAe,CAAC,OAAO;gBAC1B,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO;aAChC;SACF;QACD,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,MAAM;QACnC,QAAQ,EAAE,SAAS,EAAE,QAAQ;QAC7B,KAAK,EAAE,SAAS,EAAE,KAAK;QACvB,KAAK,EAAE,SAAS,EAAE,KAAK;KACxB,CAAC;IACF,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAEvC,OAAO;QACL,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,EAAE,SAA0B,IAAI;YAC3D,OAAO,MAAM,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,CAAC,KAOP;YACC,OAAO;gBACL,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM;gBAC9B,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,MAAM,EAAE,uBAAuB;gBAC/B,EAAE,EAAE,WAAW;gBACf,SAAS,EAAE,gBAAgB;gBAC3B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;aACpB,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,183 @@
1
+ export type EnvironmentName = "development" | "test" | "production";
2
+ export type SupportedLocale = "en" | "tr";
3
+ export type HttpMethod = "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+ export interface Clock {
5
+ now(): Date;
6
+ }
7
+ export declare const systemClock: Clock;
8
+ export interface SessionRecord {
9
+ idHash: string;
10
+ userId: string;
11
+ locale: SupportedLocale;
12
+ createdAt: string;
13
+ lastSeenAt: string;
14
+ expiresAt: string;
15
+ idleExpiresAt: string;
16
+ rotateAfter: string;
17
+ revokedAt?: string;
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+ export interface OAuthStateRecord {
21
+ stateHash: string;
22
+ returnTo: string;
23
+ locale: SupportedLocale;
24
+ createdAt: string;
25
+ expiresAt: string;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ export interface OAuthCredentialRecord {
29
+ provider: "discord" | string;
30
+ userId: string;
31
+ accessTokenCiphertext: string;
32
+ refreshTokenCiphertext: string;
33
+ scope: string;
34
+ tokenType: string;
35
+ expiresAt: string;
36
+ updatedAt: string;
37
+ }
38
+ export interface AuditActor {
39
+ type: "user" | "owner" | "system" | "anonymous";
40
+ userId?: string;
41
+ sessionIdHash?: string;
42
+ }
43
+ export interface ResourceRef {
44
+ type: string;
45
+ id: string;
46
+ }
47
+ export interface AuditEvent {
48
+ id: string;
49
+ requestId: string;
50
+ action: string;
51
+ actor: AuditActor;
52
+ resource?: ResourceRef;
53
+ result: "success" | "denied" | "failed";
54
+ reasonCode?: string;
55
+ policyVersion: number;
56
+ changes?: unknown;
57
+ metadata: {
58
+ ipHash?: string;
59
+ userAgent?: string;
60
+ durationMs: number;
61
+ };
62
+ createdAt: string;
63
+ }
64
+ export interface RealtimeEvent<T = unknown> {
65
+ version: 1;
66
+ id: string;
67
+ sequence?: number;
68
+ event: string;
69
+ channel: string;
70
+ timestamp: string;
71
+ data: T;
72
+ }
73
+ export interface OutboxRecord {
74
+ id: string;
75
+ event: RealtimeEvent;
76
+ createdAt: string;
77
+ publishedAt?: string;
78
+ attempts: number;
79
+ lastError?: string;
80
+ }
81
+ export interface BlockRecord {
82
+ subjectType: "user" | "guild" | "ip";
83
+ subjectId: string;
84
+ reason: string;
85
+ createdAt: string;
86
+ expiresAt?: string;
87
+ }
88
+ export interface MaintenanceState {
89
+ enabled: boolean;
90
+ reason?: string;
91
+ allowOwners: boolean;
92
+ updatedAt: string;
93
+ }
94
+ export interface RateLimitHitInput {
95
+ key: string;
96
+ limit: number;
97
+ windowMs: number;
98
+ nowMs: number;
99
+ cost?: number;
100
+ }
101
+ export interface RateLimitHitResult {
102
+ allowed: boolean;
103
+ limit: number;
104
+ remaining: number;
105
+ resetAtMs: number;
106
+ retryAfterMs: number;
107
+ }
108
+ export interface CacheEntry<T = unknown> {
109
+ value: T;
110
+ expiresAtMs: number;
111
+ staleUntilMs?: number;
112
+ tags: string[];
113
+ }
114
+ export type IdempotencyState = "inflight" | "completed";
115
+ export interface IdempotencyRecord {
116
+ key: string;
117
+ requestHash: string;
118
+ state: IdempotencyState;
119
+ response?: unknown;
120
+ createdAtMs: number;
121
+ expiresAtMs: number;
122
+ }
123
+ export type IdempotencyBeginResult = {
124
+ status: "started";
125
+ } | {
126
+ status: "inflight";
127
+ } | {
128
+ status: "conflict";
129
+ } | {
130
+ status: "completed";
131
+ response: unknown;
132
+ };
133
+ export interface RequestEnvelope {
134
+ method: HttpMethod;
135
+ path: string;
136
+ input: unknown;
137
+ headers?: Record<string, string | string[] | undefined>;
138
+ cookies?: Record<string, string | undefined>;
139
+ ip?: string;
140
+ origin?: string;
141
+ userAgent?: string;
142
+ requestId?: string;
143
+ locale?: SupportedLocale | string;
144
+ sessionToken?: string;
145
+ csrfToken?: string;
146
+ idempotencyKey?: string;
147
+ signal?: AbortSignal;
148
+ }
149
+ export interface SessionResolution {
150
+ session: SessionRecord | null;
151
+ replacementToken?: string;
152
+ replacementCsrfToken?: string;
153
+ clearCookie?: boolean;
154
+ }
155
+ export interface PostCommitIssue {
156
+ stage: "cache" | "realtime" | "audit";
157
+ code: string;
158
+ }
159
+ export interface ActionExecutionMeta {
160
+ requestId: string;
161
+ locale: SupportedLocale;
162
+ rateLimit?: RateLimitHitResult;
163
+ replacementSessionToken?: string;
164
+ replacementCsrfToken?: string;
165
+ clearSessionCookie?: boolean;
166
+ postCommitIssues?: PostCommitIssue[];
167
+ }
168
+ export type ActionExecutionResult<T> = {
169
+ ok: true;
170
+ data: T;
171
+ meta: ActionExecutionMeta;
172
+ } | {
173
+ ok: false;
174
+ error: {
175
+ code: string;
176
+ message: string;
177
+ status: number;
178
+ retryable: boolean;
179
+ details?: Record<string, unknown>;
180
+ };
181
+ meta: ActionExecutionMeta;
182
+ };
183
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ export const systemClock = {
2
+ now: () => new Date(),
3
+ };
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;CACtB,CAAC"}
@@ -0,0 +1,118 @@
1
+ # Writing rules
2
+
3
+ This file defines the editorial rules for GuildGate documentation, errors, examples, release notes, and repository pages.
4
+
5
+ The checklist is based on the Wikipedia essay **Signs of AI writing**, reviewed on 25 July 2026. The essay states that these signs are clues, not proof that a text was produced by a model. GuildGate uses the list as an editing checklist, not as an authorship detector.
6
+
7
+ Source: https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing
8
+
9
+ ## Content patterns to remove
10
+
11
+ 1. **Unearned importance.** Do not claim that a small function marks a major shift, leaves a lasting legacy, changes an industry, or reflects a wider movement without evidence.
12
+ 2. **Notability padding.** Do not add vague claims about press attention, experts, communities, or independent coverage. Name the source and state what it supports.
13
+ 3. **Shallow interpretation.** Do not attach generic meaning to a fact. Explain the actual effect on a request, database record, session, or operator.
14
+ 4. **Promotional wording.** Remove sales copy from technical sections. State behavior, limits, and trade-offs.
15
+ 5. **Vague attribution.** Avoid phrases such as “many developers say,” “it is widely known,” or “experts agree.” Provide a named source or omit the claim.
16
+ 6. **Generic challenge sections.** Do not add a “Challenges and future prospects” section unless it contains project-specific open work, owners, and acceptance criteria.
17
+ 7. **Topic-name repetition.** Do not treat a broad subject as a proper name and repeat it in every paragraph.
18
+ 8. **Unsupported trends.** Avoid claims about growing demand, rapid adoption, changing expectations, or industry direction unless current evidence is cited.
19
+
20
+ ## Word and sentence patterns to watch
21
+
22
+ These words are not forbidden in isolation. They require a reason. Replace them when they hide a specific verb or fact:
23
+
24
+ - additionally
25
+ - align with
26
+ - boasts
27
+ - bolstered
28
+ - crucial
29
+ - delve
30
+ - emphasizing
31
+ - enduring
32
+ - enhance
33
+ - fostering
34
+ - garner
35
+ - highlight
36
+ - interplay
37
+ - intricate
38
+ - key
39
+ - landscape
40
+ - meticulous
41
+ - pivotal
42
+ - robust
43
+ - showcase
44
+ - tapestry
45
+ - testament
46
+ - underscore
47
+ - valuable
48
+ - vibrant
49
+
50
+ Other sentence habits to remove:
51
+
52
+ - Avoiding plain forms of “is,” “has,” or “does” and replacing them with inflated verbs.
53
+ - “Not only X, but also Y,” “not X but Y,” or “X rather than Y” used as decoration.
54
+ - Three adjectives, three benefits, or three clauses added to make a sentence sound complete.
55
+ - Repeating the same noun through strained synonyms only to avoid repetition.
56
+ - Opening paragraphs with stock transitions such as “In today’s fast-paced world.”
57
+ - Closing sections with a restatement that adds no new instruction.
58
+
59
+ ## Formatting patterns to remove
60
+
61
+ - Title Case On Every Heading.
62
+ - Bold text used for ordinary words or nearly every bullet.
63
+ - A vertical list where each item starts with an inline bold heading but the items contain no useful detail.
64
+ - Repeated em dashes where commas, periods, or parentheses are clearer.
65
+ - Decorative emoji in technical pages, errors, changelogs, or security documents.
66
+ - Tables used only to make sparse content look substantial.
67
+ - Curly quotation marks copied into code, configuration, or shell commands.
68
+ - Skipped heading levels.
69
+ - Horizontal rules placed before every heading.
70
+ - Markdown syntax pasted into a surface that does not render Markdown.
71
+
72
+ ## User-directed boilerplate to remove
73
+
74
+ Do not publish assistant-like conversation fragments:
75
+
76
+ - Offers to continue, expand, rewrite, or help with another task.
77
+ - Statements about a knowledge cutoff.
78
+ - Speculation about what the reader “might want.”
79
+ - “Here is a polished version,” “certainly,” “hope this helps,” or similar framing.
80
+ - Empty placeholders such as `[insert name]`, `[add source]`, or `TODO` in a release artifact.
81
+ - Template instructions left in final text.
82
+ - Refusals, prompt references, or descriptions of how the text was generated.
83
+
84
+ ## Markup and citation artifacts to reject
85
+
86
+ Repository checks must reject or flag these before release:
87
+
88
+ - Broken Markdown fences, links, reference labels, or HTML fragments.
89
+ - Wiki markup in Markdown files.
90
+ - Internal model citation strings such as `contentReference`, `oaicite`, `turn0search`, `attributableIndex`, or `+1` citation markers.
91
+ - Provider artifacts such as Gemini citation spans, Grok cards, DeepSeek daggers, Perplexity upload references, or `:::writing` blocks.
92
+ - Nonexistent templates, categories, files, imports, commands, environment variables, or package exports.
93
+ - Tracking query strings such as `utm_source` in source links.
94
+ - DOI, ISBN, package, or documentation links that do not resolve or do not support the nearby statement.
95
+ - Citations declared but never used.
96
+
97
+ ## Repository-specific rules
98
+
99
+ 1. Use one language per file. `README.md` is English; `README.tr.md` is Turkish.
100
+ 2. Name the actor. Use “the kernel,” “the store,” “the application,” “Discord,” or “the operator” instead of “the system” when possible.
101
+ 3. Name the failure. Use `CSRF_INVALID`, `RATE_LIMITED`, or the concrete condition instead of “something went wrong.”
102
+ 4. Separate guarantees from recommendations. “Rejects HTTP origins in production” is a guarantee. “Use Redis for multiple instances” is a recommendation.
103
+ 5. State ownership. Database transactions, schema migrations, proxy trust, secret storage, and backup policy belong to the application unless a GuildGate component explicitly implements them.
104
+ 6. Avoid absolute security claims. Do not write “unhackable,” “zero risk,” “fully secure,” or “bug-free.”
105
+ 7. Examples must compile or be clearly marked as pseudocode.
106
+ 8. Every environment variable used in an example must appear in `.env.example` or be explained next to the example.
107
+ 9. Every public export used in documentation must exist in `package.json` and the built declaration files.
108
+ 10. Keep paragraphs short enough to scan, but do not split one sentence into a decorative list.
109
+
110
+ ## Release review
111
+
112
+ Before a release:
113
+
114
+ - Search the repository for the word list above and review each match.
115
+ - Search for provider citation artifacts and placeholder brackets.
116
+ - Run links, code samples, tests, type checks, package dry-run, and the doctor command.
117
+ - Read each changed page once without editing. Mark sentences that claim importance without explaining behavior.
118
+ - Ask a reviewer to check correctness, not whether the prose “sounds human.”
@@ -0,0 +1,83 @@
1
+ # Architecture
2
+
3
+ GuildGate separates policy, transport, Discord access, and storage. The core package does not import Fastify, Express, Redis, PostgreSQL, MongoDB, or a WebSocket server.
4
+
5
+ ## Components
6
+
7
+ 1. **Kernel** checks sessions, origins, CSRF tokens, blocks, maintenance state, limits, authorization, idempotency, locks, deadlines, audit records, cache tags, and realtime delivery.
8
+ 2. **Session manager** stores only a SHA-256 hash of the opaque session token. Expiry, idle expiry, rotation, caps, and revocation are enforced at resolution time.
9
+ 3. **Discord OAuth client** creates one-time state records, binds state to a browser nonce, exchanges codes, encrypts credentials, refreshes access tokens, and revokes credentials.
10
+ 4. **Discord authorizer** compares the user’s OAuth guild permissions with the bot’s current guild roles and permissions.
11
+ 5. **Store interfaces** allow each application to select storage. Durable and short-lived records can use different services.
12
+ 6. **Framework adapters** translate request and response objects without placing framework types in the core package.
13
+ 7. **Realtime hub** authenticates connections, authorizes each subscription, limits messages, handles slow clients, and closes sessions on revocation.
14
+ 8. **Outbox dispatcher** publishes committed events from a durable outbox implementation.
15
+
16
+ The component diagram is in [`uml/components.mmd`](./uml/components.mmd).
17
+
18
+ ## Action order
19
+
20
+ The default order for an unsafe authenticated request is:
21
+
22
+ 1. Resolve and, when due, rotate the session.
23
+ 2. Check the exact request origin.
24
+ 3. Validate the CSRF token against the session hash.
25
+ 4. Parse input.
26
+ 5. Resolve the protected resource.
27
+ 6. Apply maintenance and block policies.
28
+ 7. Apply the action rate limit.
29
+ 8. Run application or Discord authorization.
30
+ 9. Reserve the idempotency key.
31
+ 10. Acquire the resource lock.
32
+ 11. Run the operation under one deadline.
33
+ 12. Complete the idempotency record before non-transactional follow-up work.
34
+ 13. Invalidate cache tags.
35
+ 14. Enqueue or publish realtime events.
36
+ 15. Write the audit event.
37
+ 16. Release the lock.
38
+
39
+ See [`uml/action-sequence.mmd`](./uml/action-sequence.mmd).
40
+
41
+ ## Transaction boundary
42
+
43
+ GuildGate cannot create a transaction for an unknown database driver. The action’s `execute()` function owns the database transaction.
44
+
45
+ For a setting write, the application should place these changes in one transaction where possible:
46
+
47
+ - Read or compare the current revision.
48
+ - Write the new setting record.
49
+ - Write the application’s domain audit record if it must be atomic with the setting.
50
+ - Write a durable outbox row.
51
+
52
+ GuildGate’s general audit store runs after `execute()`. Use `audit.failClosedActions` only when refusing the response after an audit failure is useful. It cannot undo a transaction that has already committed. A high-risk operation that requires atomic audit must write its domain audit inside `execute()`.
53
+
54
+ ## Consistency choices
55
+
56
+ - **Session and OAuth state:** consistent, single-use, fail closed.
57
+ - **Idempotency and distributed locks:** atomic store operations are required for multiple instances.
58
+ - **Guild display data:** short cache duration is acceptable.
59
+ - **Permission-sensitive writes:** use live Discord checks or a bot gateway cache that is invalidated by member and role events.
60
+ - **Audit and outbox:** durable storage is recommended.
61
+ - **Realtime delivery:** an event may be delivered after commit. Use an outbox when delivery must survive process restarts.
62
+
63
+ ## Failure policy
64
+
65
+ The kernel fails closed when it cannot establish authentication, CSRF validity, origin validity, a block policy, a required rate limit, authorization, idempotency ownership, or a resource lock.
66
+
67
+ The application chooses what to do when cache, audit, or realtime delivery fails. Current action behavior is:
68
+
69
+ - Cache invalidation, realtime publication, and outbox enqueue failures are returned as `meta.postCommitIssues`; the committed operation is not repeated.
70
+ - General audit failure is also reported as a post-commit issue. It fails the response only when the action name is in `audit.failClosedActions`.
71
+ - An audit fail-closed action must use idempotency so a client retry cannot repeat an operation that may already be committed.
72
+
73
+ A production adapter can add retries, queues, or driver-specific handling around these interfaces.
74
+
75
+ ## Trust boundaries
76
+
77
+ - The browser is untrusted.
78
+ - Route parameters, body fields, headers, locale values, and channel names are untrusted.
79
+ - OAuth access does not grant bot permissions.
80
+ - A cached guild list is not permanent authorization.
81
+ - A reverse proxy header is trusted only when the application has configured the proxy chain.
82
+ - Store implementations are trusted code and must preserve atomic semantics stated by their interfaces.
83
+ - Realtime transport libraries are responsible for TLS termination and frame parsing. The GuildGate hub starts after the transport accepts a connection.