@maudecode/kanvil 0.4.0 → 0.4.1

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.
@@ -10,6 +10,8 @@ PORT=5180
10
10
  KANVIL_TRUSTED_ORIGINS=http://localhost:5173
11
11
  # Write requests per minute allowed for one signed-in account before 429 responses.
12
12
  KANVIL_SESSION_WRITE_LIMIT=120
13
+ # Set false to disable rate limiting for all API keys, including existing keys.
14
+ KANVIL_API_KEY_RATE_LIMIT_ENABLED=true
13
15
  # Configure all three OIDC values together. Omit them to use local login only.
14
16
  # OIDC_DISCOVERY_URL=https://identity.example.com/.well-known/openid-configuration
15
17
  # OIDC_CLIENT_ID=
@@ -1,4 +1,4 @@
1
- import { a as auth_schema_exports, c as oauthConsent, d as user, i as apikey, l as oauthRefreshToken, o as oauthAccessToken, r as account, s as oauthClient, u as session } from "./config-DtNfVSnB.mjs";
1
+ import { a as auth_schema_exports, c as oauthConsent, d as user, i as apikey, l as oauthRefreshToken, o as oauthAccessToken, r as account, s as oauthClient, u as session } from "./config-BFzcAs8u.mjs";
2
2
  import { c as searchAuthentication, i as instanceUpdateSchema, l as searchSettingsSchema, n as authenticationPolicySchema, r as brokerSettingsSchema, s as s3SettingsSchema } from "./instance-CCeXRJ6m.mjs";
3
3
  import { $ as permissions, C as eventLabels, Ct as ticketFilter, D as formatEvent, Dt as workflowOrder, E as filterList, Et as ticketSchema, J as parseCustomFieldFilter, K as operationPermission, L as isSeededRole, M as invitationStatuses, N as isHexColor, O as hasAdminRole, Ot as workspaceSchema, Q as permissionScope, S as eventCategories, St as ticketExportFormat, T as filterKey, Tt as ticketPredicates, X as parsePublicOutput, Y as parseOutput, Z as permissionLabels, _ as deliverySettingsSchema, _t as snapshotSchema, at as remoteKinds, b as difficulties, bt as states, c as credentialProjectsSchema, ct as repositorySecretFields, d as customFieldValue, dt as roleLabels, et as priorities, f as customFieldValueSchema, ft as roles, g as deliverySecretFields, gt as signInMethodLabels, h as deletedAccountName, ht as seededPermissions, i as channelRouteAllows, j as importSecretFields, l as customFieldFilterTokens, lt as resourceProvider, m as defaultWorkflow, mt as searchResultTypes, n as attachmentLimit, nt as projectSchema, o as closedStates, ot as repositoryProviderDefinitions, p as defaultOrder, pt as scopes, q as operations, r as channelKinds, rt as publicOperations, st as repositoryProviders, t as ApiError, tt as priorityLabels, u as customFieldSchema, ut as roleCategory, v as destinationEventKeys, vt as stagedUploadLifetime, w as eventSchema, wt as ticketImportSchema, y as destinationKinds, yt as stateLabels, z as mentionScheme } from "./src-tQ82UI8k.mjs";
4
4
  import { and, asc, count, desc, eq, getTableColumns, gt, ilike, inArray, isNotNull, isNull, lt, lte, ne, notInArray, or, sql } from "drizzle-orm";
@@ -7711,6 +7711,7 @@ function createAuth(database, config) {
7711
7711
  defaultPrefix: "kv_",
7712
7712
  enableMetadata: true,
7713
7713
  maximumNameLength: 100,
7714
+ rateLimit: { enabled: config.KANVIL_API_KEY_RATE_LIMIT_ENABLED },
7714
7715
  keyExpiration: {
7715
7716
  minExpiresIn: 1 / 24,
7716
7717
  maxExpiresIn: 365
@@ -395,6 +395,7 @@ const envSchema = z.object({
395
395
  DISCORD_CLIENT_ID: optionalEnv(z.string().min(1)),
396
396
  DISCORD_CLIENT_SECRET: optionalEnv(z.string().min(1)),
397
397
  KANVIL_SESSION_WRITE_LIMIT: z.coerce.number().int().min(1).max(1e5).default(120),
398
+ KANVIL_API_KEY_RATE_LIMIT_ENABLED: z.enum(["true", "false"]).default("true").transform((value) => value === "true"),
398
399
  KANVIL_PUSH_ENABLED: z.enum(["true", "false"]).default("false").transform((value) => value === "true"),
399
400
  EXPO_ACCESS_TOKEN: optionalEnv(z.string().min(1)),
400
401
  SMTP_URL: optionalEnv(z.url({ protocol: /^smtps?$/ })),
@@ -1,7 +1,7 @@
1
- import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DtNfVSnB.mjs";
1
+ import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-BFzcAs8u.mjs";
2
2
  import { a as photoInputSchema, i as instanceUpdateSchema, l as searchSettingsSchema, o as photoResultSchema, r as brokerSettingsSchema, s as s3SettingsSchema, t as adminInstanceSchema } from "./instance-CCeXRJ6m.mjs";
3
3
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
4
- import { $ as subscriptions, A as createTicketSearch, B as blobObjects, C as DeliveryError, D as requireInstanceAdmin, E as createWriteLimiter, F as stagedUploadCutoff, G as members, H as eventClock, I as createObjectStore, J as profilePhotos, K as notificationPreferences, L as createPollingJob, M as createBlobStorage, N as decodeImage, O as requireSession, P as imageResponse, Q as storageBackends, R as attachmentContent, S as deliverBatch, T as createAccess, U as events, V as deliveryCheckpoints, X as pushDevices, Y as projects, Z as stagedUploads, _ as recipientColumns, a as deliverySecrets, b as receiveRepositoryWebhook, c as ownerBlocker, d as createDomain, et as tickets, f as fail, g as reachable, h as one, i as createInstance, j as createTicketIndex, k as readCalendarFeed, l as appendEvents, m as notify, n as emailCodesAvailable, nt as workspaces, o as otherActiveAdministrator, p as json, q as notifications, r as signInProviders, rt as matchesOIDCBinding, s as ownedWorkspaces, t as createAuth, tt as workspaceMembers, u as assertVersion, v as requireRow, w as createHttpSender, x as defaultSenders, y as processImports, z as attachments } from "./auth-Cuo7ECLh.mjs";
4
+ import { $ as subscriptions, A as createTicketSearch, B as blobObjects, C as DeliveryError, D as requireInstanceAdmin, E as createWriteLimiter, F as stagedUploadCutoff, G as members, H as eventClock, I as createObjectStore, J as profilePhotos, K as notificationPreferences, L as createPollingJob, M as createBlobStorage, N as decodeImage, O as requireSession, P as imageResponse, Q as storageBackends, R as attachmentContent, S as deliverBatch, T as createAccess, U as events, V as deliveryCheckpoints, X as pushDevices, Y as projects, Z as stagedUploads, _ as recipientColumns, a as deliverySecrets, b as receiveRepositoryWebhook, c as ownerBlocker, d as createDomain, et as tickets, f as fail, g as reachable, h as one, i as createInstance, j as createTicketIndex, k as readCalendarFeed, l as appendEvents, m as notify, n as emailCodesAvailable, nt as workspaces, o as otherActiveAdministrator, p as json, q as notifications, r as signInProviders, rt as matchesOIDCBinding, s as ownedWorkspaces, t as createAuth, tt as workspaceMembers, u as assertVersion, v as requireRow, w as createHttpSender, x as defaultSenders, y as processImports, z as attachments } from "./auth-DAqfvUJm.mjs";
5
5
  import { A as importProviderDefinitions, B as notificationIdsInput, F as isPresetColor, G as okSchema, H as notificationPreferencesSchema, I as isPublicOperation, O as hasAdminRole, P as isOperation, R as memberSchema, St as ticketExportFormat, U as notificationReadInput, V as notificationPreferencesInput, ft as roles, it as pushDeviceInput, k as importLimits, m as defaultWorkflow, o as closedStates, pt as scopes, q as operations, rt as publicOperations, s as credentialInput, t as ApiError, x as errorSchema, yt as stateLabels } from "./src-tQ82UI8k.mjs";
6
6
  import { serve } from "@hono/node-server";
7
7
  import pg from "pg";
@@ -326,7 +326,7 @@ function createAccountAdministration(database, auth, config) {
326
326
  }
327
327
  //#endregion
328
328
  //#region src/version.ts
329
- const applicationVersion = "0.4.0";
329
+ const applicationVersion = "0.4.1";
330
330
  //#endregion
331
331
  //#region src/realtime.ts
332
332
  async function createRealtime(config) {
@@ -1,4 +1,4 @@
1
- import { n as createDatabase, t as readConfig } from "./config-DtNfVSnB.mjs";
1
+ import { n as createDatabase, t as readConfig } from "./config-BFzcAs8u.mjs";
2
2
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
3
3
  //#region src/cli/migrate.ts
4
4
  const database = createDatabase(readConfig().DATABASE_URL);
@@ -1,5 +1,5 @@
1
- import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DtNfVSnB.mjs";
2
- import { W as instanceSettings, f as fail, t as createAuth } from "./auth-Cuo7ECLh.mjs";
1
+ import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-BFzcAs8u.mjs";
2
+ import { W as instanceSettings, f as fail, t as createAuth } from "./auth-DAqfvUJm.mjs";
3
3
  import { O as hasAdminRole } from "./src-tQ82UI8k.mjs";
4
4
  import { and, eq, isNull, sql } from "drizzle-orm";
5
5
  import { z } from "zod";
package/compose.yml CHANGED
@@ -12,6 +12,7 @@ services:
12
12
  KANVIL_SETUP_TOKEN: ${KANVIL_SETUP_TOKEN:-}
13
13
  KANVIL_URL: ${KANVIL_URL:?Set your public HTTPS URL}
14
14
  KANVIL_TRUSTED_ORIGINS: ${KANVIL_TRUSTED_ORIGINS:-}
15
+ KANVIL_API_KEY_RATE_LIMIT_ENABLED: ${KANVIL_API_KEY_RATE_LIMIT_ENABLED:-true}
15
16
  OIDC_DISCOVERY_URL: ${OIDC_DISCOVERY_URL:-}
16
17
  OIDC_CLIENT_ID: ${OIDC_CLIENT_ID:-}
17
18
  OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET:-}
package/docs/api.md CHANGED
@@ -16,7 +16,7 @@ Use the ticket's current `version` as `expectedVersion` for updates. A stale ver
16
16
 
17
17
  Reuse the same UUID `requestId` when retrying a write. Reusing it with a different operation or payload returns `409 idempotency_conflict`. Bulk edits either all commit or all roll back, and publish one `ticket.updated` event per ticket with consecutive IDs in a single batch.
18
18
 
19
- Signed-in sessions may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) to `/api/v1`, and MCP OAuth clients get the same budget for write tools; exceeding it returns `429 rate_limited` with a `Retry-After` header. API keys keep the rate limit applied by the authentication layer. The counter is per application process.
19
+ Signed-in sessions may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) to `/api/v1`, and MCP OAuth clients get the same budget for write tools; exceeding it returns `429 rate_limited` with a `Retry-After` header. This counter is per application process. API keys have a separate limit of 600 requests per minute per key across HTTP and MCP. An instance operator can set `KANVIL_API_KEY_RATE_LIMIT_ENABLED=false` and restart the server to disable that limit for existing and new keys. The default is `true`; restoring it enforces the limit again. Session, MCP OAuth, and sign-in limits are unchanged, as are key expiration, revocation, scopes, and account permissions.
20
20
 
21
21
  Claims require an eligible ticket and current version. Successful claiming moves it to In progress. Renew an unexpired claim using its ID and the same credential. Once a ticket has been claimed, machine ticket updates and evidence submissions always require a current matching claim ID, including after release or recovery. Submit completion and evidence before releasing ownership. Human sessions can edit and reschedule work without a machine claim. Release unexpired ownership after the worker stops or completes. The ticket response exposes `canReleaseClaim` for the exact credential or client identity; sharing an account does not confer ownership. An expired claim remains blocking until a project administrator confirms the old worker stopped and explicitly recovers it; recovery does not automatically reschedule work.
22
22
 
@@ -91,7 +91,9 @@ location / {
91
91
 
92
92
  Keep `KANVIL_URL` equal to the public HTTPS origin. Add `KANVIL_TRUSTED_ORIGINS` only for additional browser clients you explicitly operate. Web, API, MCP, login, consent, and realtime use the same application origin. No separate message broker or search service is required.
93
93
 
94
- Signed-in sessions and MCP OAuth clients may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) before receiving `429 rate_limited`; API keys are limited separately by the authentication layer. The counter is per application process.
94
+ Signed-in sessions and MCP OAuth clients may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) before receiving `429 rate_limited`. This counter is per application process.
95
+
96
+ API keys have a separate limit of 600 requests per minute per key across HTTP and MCP. Set `KANVIL_API_KEY_RATE_LIMIT_ENABLED=false` in `.env` and restart the server (with Compose, `docker compose up -d app`) to disable that limit for every existing and new API key. Restore `true` to enforce it again. This setting does not change session, MCP OAuth, or sign-in limits, and key expiration, revocation, scopes, and account permissions still apply.
95
97
 
96
98
  ## PocketID / OIDC
97
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maudecode/kanvil",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Open-source, self-hosted ticketing for agents and people.",
5
5
  "license": "MIT",
6
6
  "engines": {