ต้นแบบ · รุ่นทดลอง: มีทุก server ในทะเบียนทางการ อัปเดตทุก 30 นาที รีวิวและบัญชีผู้ใช้จะมาในขั้นถัดไป
PopNx MCPที่รวม MCP สำหรับคนใช้ AI · อธิบายเป็นภาษาคน

Slotlock

โดย PILOTAAIรุ่น 0.6.0i★ –iรันบนเครื่อง
ชื่อใน registryio.github.PILOTAAI/slotlock

ติดตั้ง

ภาพรวม

คำอธิบายจากผู้ทำ (ภาษาอังกฤษตามต้นฉบับ)

Calendars for AI agents: free/busy, next free slot, and bookings PostgreSQL never double-books.

สามเสาของ PopNx MCP

สามเรื่องนี้ไม่รวมเป็นคะแนนเดียว เพราะความเห็น การวัด และความนิยม เป็นคนละเรื่องกัน

ใช้ทำอะไรได้

ตัวนี้ยังไม่ได้เรียบเรียงเป็นภาษาไทย ด้านล่างคือข้อเท็จจริงที่เราอ่านจาก registry

  • การเชื่อมต่อ: รันบนเครื่อง
  • ช่องทาง (transport): Streamable HTTP
  • หมวด: เกม กีฬา อาหาร และการเรียน
  • ต้องใส่ค่าเหล่านี้: DATABASE_URL, SLOTLOCK_AUTH_TOKEN, SLOTLOCK_CONFIRMATION_SECRET, SLOTLOCK_METRICS_TOKEN, SLOTLOCK_MIGRATE_DATABASE_URL

ติดตั้ง

เลือกโปรแกรม AI ที่คุณใช้ แล้วคัดลอกค่าตั้งไปวาง

ก่อนติดตั้ง

  • ผลตรวจเบื้องต้น: ผ่าน 1 จาก 1 ข้อ
  • รันในเครื่องคุณด้วยสิทธิ์ของผู้ใช้คุณ (ไฟล์ อินเทอร์เน็ต แอปต่าง ๆ)
  • ค่าที่คุณต้องใส่เอง: DATABASE_URL, SLOTLOCK_AUTH_TOKEN, SLOTLOCK_CONFIRMATION_SECRET, SLOTLOCK_METRICS_TOKEN, PORT, SLOTLOCK_MIGRATE_DATABASE_URL, SLOTLOCK_CONFIRM_WRITES, SLOTLOCK_AVAILABILITY, SLOTLOCK_REST_API, SLOTLOCK_RETENTION_DAYS, SLOTLOCK_SHARED_LIMITS, SLOTLOCK_WEBHOOKS, SLOTLOCK_TENANT

ค่าที่ติดป้ายความลับคือกุญแจของบัญชีคุณ อย่าส่งให้ใคร และอย่าวางในแชต

ต้องเตรียมก่อน

  • Dockerติดตั้ง Docker ในเครื่อง

คุณใช้แอปไหน

เลือกแอปด้านบน เราจำไว้ให้ ครั้งหน้าปุ่มจะขึ้นว่า “ติดตั้งลง …”

เปิด Claude Desktop → Settings → Developer → Edit Config แล้ววางค่านี้ในไฟล์ claude_desktop_config.json

{
  "mcpServers": {
    "slotlock": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "DATABASE_URL",
        "-e",
        "SLOTLOCK_AUTH_TOKEN",
        "-e",
        "SLOTLOCK_CONFIRMATION_SECRET",
        "-e",
        "SLOTLOCK_METRICS_TOKEN",
        "-e",
        "PORT",
        "-e",
        "SLOTLOCK_MIGRATE_DATABASE_URL",
        "-e",
        "SLOTLOCK_CONFIRM_WRITES",
        "-e",
        "SLOTLOCK_AVAILABILITY",
        "-e",
        "SLOTLOCK_REST_API",
        "-e",
        "SLOTLOCK_RETENTION_DAYS",
        "-e",
        "SLOTLOCK_SHARED_LIMITS",
        "-e",
        "SLOTLOCK_WEBHOOKS",
        "-e",
        "SLOTLOCK_TENANT",
        "ghcr.io/pilotaai/slotlock:0.6.0",
        "serve",
        "--migrate"
      ],
      "env": {
        "DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_AUTH_TOKEN": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRMATION_SECRET": "ใส่ค่าของคุณ",
        "SLOTLOCK_METRICS_TOKEN": "ใส่ค่าของคุณ",
        "PORT": "ใส่ค่าของคุณ",
        "SLOTLOCK_MIGRATE_DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRM_WRITES": "ใส่ค่าของคุณ",
        "SLOTLOCK_AVAILABILITY": "ใส่ค่าของคุณ",
        "SLOTLOCK_REST_API": "ใส่ค่าของคุณ",
        "SLOTLOCK_RETENTION_DAYS": "ใส่ค่าของคุณ",
        "SLOTLOCK_SHARED_LIMITS": "ใส่ค่าของคุณ",
        "SLOTLOCK_WEBHOOKS": "ใส่ค่าของคุณ",
        "SLOTLOCK_TENANT": "ใส่ค่าของคุณ"
      }
    }
  }
}

พิมพ์คำสั่งนี้ใน Terminal

claude mcp add slotlock --env DATABASE_URL='ใส่ค่าของคุณ' --env SLOTLOCK_AUTH_TOKEN='ใส่ค่าของคุณ' --env SLOTLOCK_CONFIRMATION_SECRET='ใส่ค่าของคุณ' --env SLOTLOCK_METRICS_TOKEN='ใส่ค่าของคุณ' --env PORT='ใส่ค่าของคุณ' --env SLOTLOCK_MIGRATE_DATABASE_URL='ใส่ค่าของคุณ' --env SLOTLOCK_CONFIRM_WRITES='ใส่ค่าของคุณ' --env SLOTLOCK_AVAILABILITY='ใส่ค่าของคุณ' --env SLOTLOCK_REST_API='ใส่ค่าของคุณ' --env SLOTLOCK_RETENTION_DAYS='ใส่ค่าของคุณ' --env SLOTLOCK_SHARED_LIMITS='ใส่ค่าของคุณ' --env SLOTLOCK_WEBHOOKS='ใส่ค่าของคุณ' --env SLOTLOCK_TENANT='ใส่ค่าของคุณ' -- docker run -i --rm -e DATABASE_URL -e SLOTLOCK_AUTH_TOKEN -e SLOTLOCK_CONFIRMATION_SECRET -e SLOTLOCK_METRICS_TOKEN -e PORT -e SLOTLOCK_MIGRATE_DATABASE_URL -e SLOTLOCK_CONFIRM_WRITES -e SLOTLOCK_AVAILABILITY -e SLOTLOCK_REST_API -e SLOTLOCK_RETENTION_DAYS -e SLOTLOCK_SHARED_LIMITS -e SLOTLOCK_WEBHOOKS -e SLOTLOCK_TENANT ghcr.io/pilotaai/slotlock:0.6.0 serve --migrate

เติมค่าที่เว้นว่างไว้ก่อนรัน

เปิดใน Cursor

หรือคัดลอกค่าตั้งเอง

วางในไฟล์ .cursor/mcp.json ของโปรเจกต์ หรือ ~/.cursor/mcp.json

{
  "mcpServers": {
    "slotlock": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "DATABASE_URL",
        "-e",
        "SLOTLOCK_AUTH_TOKEN",
        "-e",
        "SLOTLOCK_CONFIRMATION_SECRET",
        "-e",
        "SLOTLOCK_METRICS_TOKEN",
        "-e",
        "PORT",
        "-e",
        "SLOTLOCK_MIGRATE_DATABASE_URL",
        "-e",
        "SLOTLOCK_CONFIRM_WRITES",
        "-e",
        "SLOTLOCK_AVAILABILITY",
        "-e",
        "SLOTLOCK_REST_API",
        "-e",
        "SLOTLOCK_RETENTION_DAYS",
        "-e",
        "SLOTLOCK_SHARED_LIMITS",
        "-e",
        "SLOTLOCK_WEBHOOKS",
        "-e",
        "SLOTLOCK_TENANT",
        "ghcr.io/pilotaai/slotlock:0.6.0",
        "serve",
        "--migrate"
      ],
      "env": {
        "DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_AUTH_TOKEN": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRMATION_SECRET": "ใส่ค่าของคุณ",
        "SLOTLOCK_METRICS_TOKEN": "ใส่ค่าของคุณ",
        "PORT": "ใส่ค่าของคุณ",
        "SLOTLOCK_MIGRATE_DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRM_WRITES": "ใส่ค่าของคุณ",
        "SLOTLOCK_AVAILABILITY": "ใส่ค่าของคุณ",
        "SLOTLOCK_REST_API": "ใส่ค่าของคุณ",
        "SLOTLOCK_RETENTION_DAYS": "ใส่ค่าของคุณ",
        "SLOTLOCK_SHARED_LIMITS": "ใส่ค่าของคุณ",
        "SLOTLOCK_WEBHOOKS": "ใส่ค่าของคุณ",
        "SLOTLOCK_TENANT": "ใส่ค่าของคุณ"
      }
    }
  }
}

เบราว์เซอร์จะถามก่อนเปิดแอป จากนั้นแอปแสดงค่าตั้งให้ตรวจแล้วกดยืนยัน ช่อง key เว้นว่างไว้ให้คุณใส่เอง

เปิดใน VS Code

หรือคัดลอกค่าตั้งเอง

วางในไฟล์ .vscode/mcp.json ของโปรเจกต์

{
  "servers": {
    "slotlock": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "DATABASE_URL",
        "-e",
        "SLOTLOCK_AUTH_TOKEN",
        "-e",
        "SLOTLOCK_CONFIRMATION_SECRET",
        "-e",
        "SLOTLOCK_METRICS_TOKEN",
        "-e",
        "PORT",
        "-e",
        "SLOTLOCK_MIGRATE_DATABASE_URL",
        "-e",
        "SLOTLOCK_CONFIRM_WRITES",
        "-e",
        "SLOTLOCK_AVAILABILITY",
        "-e",
        "SLOTLOCK_REST_API",
        "-e",
        "SLOTLOCK_RETENTION_DAYS",
        "-e",
        "SLOTLOCK_SHARED_LIMITS",
        "-e",
        "SLOTLOCK_WEBHOOKS",
        "-e",
        "SLOTLOCK_TENANT",
        "ghcr.io/pilotaai/slotlock:0.6.0",
        "serve",
        "--migrate"
      ],
      "env": {
        "DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_AUTH_TOKEN": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRMATION_SECRET": "ใส่ค่าของคุณ",
        "SLOTLOCK_METRICS_TOKEN": "ใส่ค่าของคุณ",
        "PORT": "ใส่ค่าของคุณ",
        "SLOTLOCK_MIGRATE_DATABASE_URL": "ใส่ค่าของคุณ",
        "SLOTLOCK_CONFIRM_WRITES": "ใส่ค่าของคุณ",
        "SLOTLOCK_AVAILABILITY": "ใส่ค่าของคุณ",
        "SLOTLOCK_REST_API": "ใส่ค่าของคุณ",
        "SLOTLOCK_RETENTION_DAYS": "ใส่ค่าของคุณ",
        "SLOTLOCK_SHARED_LIMITS": "ใส่ค่าของคุณ",
        "SLOTLOCK_WEBHOOKS": "ใส่ค่าของคุณ",
        "SLOTLOCK_TENANT": "ใส่ค่าของคุณ"
      }
    }
  }
}

เบราว์เซอร์จะถามก่อนเปิดแอป จากนั้นแอปแสดงค่าตั้งให้ตรวจแล้วกดยืนยัน ช่อง key เว้นว่างไว้ให้คุณใส่เอง

ตัวนี้รันในเครื่องคุณ แต่ ChatGPT ต่อได้เฉพาะ server บนอินเทอร์เน็ต ลองเลือกแอปอื่น

ตัวเลือกเก็บในคุกกี้บนเครื่องนี้ ไม่ต้องมีบัญชี

ค่าที่ต้องใส่เอง

  • DATABASE_URL ความลับ จำเป็น env
    postgres:// URL of a PostgreSQL 16 or newer database the container can reach, as a role that is not a superuser and has no BYPASSRLS: serve refuses one, since row-level security would not confine it.…
  • SLOTLOCK_AUTH_TOKEN ความลับ env
    Optional bearer token for SLOTLOCK_TENANT, with read and write (never admin): at least 32 random characters (openssl rand -hex 32). Leave it unset to accept only API keys (slotlock key create).
  • SLOTLOCK_CONFIRMATION_SECRET ความลับ env
    Signs pending write confirmations: at least 32 random characters, different from the token. Required unless SLOTLOCK_CONFIRM_WRITES is none.
  • SLOTLOCK_METRICS_TOKEN ความลับ env
    Serves Prometheus metrics at /metrics to this bearer token: at least 32 random characters, different from every other secret. Off without it.
  • PORT env
    Port the server listens on inside the container.
  • SLOTLOCK_MIGRATE_DATABASE_URL ความลับ env
    postgres:// URL of the schema owner. When set, migration runs as this role and grants the DATABASE_URL role only what it needs at runtime.
  • SLOTLOCK_CONFIRM_WRITES env
    Writes that wait for a person to confirm them: none, all, or a comma-separated list of slotlock_create_event, slotlock_book_together, slotlock_book_pool, slotlock_update_event, slotlock_delete_event.…
  • SLOTLOCK_AVAILABILITY env
    JSON array of weekly bookable-hours rules, e.g. [{"rrule":"FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR","startMinutes":540,"durationMinutes":480}]. Without rules no slot is ever offered.
  • SLOTLOCK_REST_API env
    on: also serve the tools as a REST API under /v1, and the application API (resources, bookable hours, holds, reservations, coverage, maintenance) for admin keys, described by an OpenAPI 3.1 document…
  • SLOTLOCK_RETENTION_DAYS env
    Days of history kept: the change feed (who made each change included) and how far back a cursor reaches, and ended waitlist entries; retried writes are answered for at least 30 days whatever it is. 1…
  • SLOTLOCK_SHARED_LIMITS env
    on: tenant request budgets (SLOTLOCK_LIMITS' tenant_* and app_*) are shared by every server of a deployment through PostgreSQL, so N servers allow a tenant its rate once, not N times. off: each serve…
  • SLOTLOCK_WEBHOOKS env
    on: deliver each tenant's changes to its webhooks (POST /v1/webhooks), signed as Standard Webhooks; never to a private network address. private: those too, for testing. off: keep the change feed (GET…
  • SLOTLOCK_TENANT env
    The tenant SLOTLOCK_AUTH_TOKEN acts in; API keys act in their own tenant.

ค่าตั้งนี้สร้างจากข้อมูลใน registry ของผู้ทำ ถ้าไม่ตรงกับ README ให้เชื่อ README

ทำอะไรได้บ้าง

รันในเครื่องคุณ รายการเครื่องมือจะขึ้นในแอป AI หลังติดตั้ง

ผลตรวจเบื้องต้นi

เราตรวจจากข้อมูลที่ใครก็เปิดดูได้ ยังไม่ได้อ่านโค้ด ข้อที่ไม่เกี่ยวกับตัวนี้ไม่นับในตัวหาร · ตรวจเมื่อ 7 พ.ย. 2569

  • ✓ ผ่าน

    ตัวตนผู้ทำตรงกับ repo

    ชื่อใน registry ยืนยันด้วยบัญชี GitHub PILOTAAI ซึ่งเป็นเจ้าของ repo

  • … ยังไม่ตรวจ

    ยังมีคนดูแล

    ยังไม่ได้อ่านข้อมูล repo จาก GitHub

  • … ยังไม่ตรวจ

    มีสัญญาอนุญาต (license)

    ยังไม่ได้อ่านข้อมูล repo จาก GitHub

  • – ไม่เกี่ยว

    endpoint ตอบ

    ตัวนี้รันบนเครื่องคุณ ไม่มี endpoint บนอินเทอร์เน็ต

  • – ไม่เกี่ยว

    ล็อกอินแบบ OAuth

    ตัวนี้รันบนเครื่อง ใช้ค่าที่ตั้งในเครื่องแทน

  • … ยังไม่ตรวจ

    คำอธิบายเครื่องมือไม่มีคำสั่งแอบแฝง

    รันในเครื่องคุณ เราอ่านรายการเครื่องมือได้เฉพาะตัวที่มี endpoint บนอินเทอร์เน็ต

เทียบกับตัวอื่นในหมวด เกม กีฬา อาหาร และการเรียนi

  • ดาวมากกว่า 0% ของอีก 1282 ตัวในหมวดนี้
  • ส่งรุ่นใหม่เข้า registry ล่าสุดกว่า 79% ของอีก 1282 ตัวในหมวดนี้
  • ผลตรวจเบื้องต้นดีกว่าหรือเท่ากับ 100% ของอีก 1282 ตัวในหมวดนี้

เทียบกับทุก server ที่ยังใช้งานในหมวดนี้ทั้งทะเบียน ดาวนับเฉพาะเมื่อผู้ลงทะเบียนเป็นเจ้าของ repo

คำถามที่พบบ่อย

Slotlock ปลอดภัยไหม?

จากการตรวจเบื้องต้นของเรา ผ่าน 1 จาก 1 ข้อที่ตรวจได้ เรายังไม่ได้อ่านโค้ด ก่อนใส่กุญแจหรือ token ให้ดูว่า server ขอสิทธิ์อะไร และเลือกกุญแจที่ทำได้เท่าที่จำเป็น

ต้องใช้กุญแจหรือรหัสอะไรบ้าง?

ต้องใส่ค่าเหล่านี้: DATABASE_URL, SLOTLOCK_AUTH_TOKEN, SLOTLOCK_CONFIRMATION_SECRET, SLOTLOCK_METRICS_TOKEN, SLOTLOCK_MIGRATE_DATABASE_URL

ตัวนี้รันบนเครื่องหรือบนคลาวด์?

รันบนเครื่องของคุณ โปรแกรม AI จะเปิด server ขึ้นมาเองเมื่อใช้ ข้อมูลไม่ต้องผ่านเซิร์ฟเวอร์ของผู้ทำ (ยกเว้นตัวที่ไปเรียกบริการออนไลน์ต่อ)

ยังมีคนดูแลอยู่ไหม?

repo บน GitHub มีการแก้โค้ดล่าสุด – และส่งรุ่นล่าสุด (0.6.0) เข้า registry 9 วันที่ผ่านมา

ใช้กับโปรแกรมไหนได้บ้าง?

โปรแกรมที่รองรับ MCP เช่น Claude Desktop, Claude Code, Cursor และ VS Code ตัวนี้ต้องมี Docker ในเครื่องด้วย

มีตัวไหนใช้แทนได้บ้าง?

ในหมวดเดียวกันมี فکرستان (Fekrestan), IBOL Open Learning: Albert Tan Sin Hon, G Network Australian Lotto Results

ตัวที่ใช้แทนได้

server หมวดเดียวกันจากผู้ทำคนอื่น เรียงตามดาว

อ่านการ์ด:★ ดาว GitHubiสามเสา: รีวิว · ผลตรวจ · การใช้งานiอัปเดต = วันที่ส่งรุ่นล่าสุดเข้า registryi

فکرستان (Fekrestan)

โดย fekrestan.com

Public pages of fekrestan.com (Persian learning, grades 3–9) for parents and teachers. Read-only.

รันบนคลาวด์อัปเดต วันนี้
ติดตั้ง

README จากผู้ทำ

เปิดอ่าน README

ส่วนนี้คัดจาก repo ของผู้ทำตามต้นฉบับ PopNx MCP ไม่ได้ตรวจหรือรับรองเนื้อหา ตัด HTML รูปภาพ และสคริปต์ออกแล้ว ลิงก์จะพาออกนอกเว็บ

Slotlock

Slotlock is a calendar for AI agents. It books vehicles, rooms, machines, people or anything else reservable, answers "when is this free?" from weekly availability rules, and lets PostgreSQL reject double bookings, however many writers race. Agents call it over MCP or A2A; your code calls it as a TypeScript library.

Why it is built for agents

  • Resources own calendars, not users, and the tenant comes only from the authenticated caller.
  • Intervals are half-open [start, end), so back-to-back bookings never collide.
  • A PostgreSQL exclusion constraint decides overlaps; a conflict comes back as data, not an error.
  • Writes are idempotent and take an expected revision.
  • Free/busy says whether it is *certain*: a calendar not yet synced makes time unproven, not free.
  • Tool names, schemas and errors work on Claude, OpenAI and every MCP host.

Install

npm install slotlock postgres

Node.js 22.12+, PostgreSQL 16 or newer with btree_gist (created for you if missing; CI runs 16, 17 and 18), and postgres ^3.4.5 as a peer dependency. TypeScript declarations are included.

Self-host with Docker

From a clone of this repository:

scripts/quickstart-env.sh > .env    # .env.example, its four secrets set to openssl rand -hex 32

docker compose up --build --detach docker compose exec slotlock slotlock resource add vehicle-42 --timezone Europe/London

URLWhat
http://localhost:8080/mcpMCP, with Authorization: Bearer
http://localhost:8080/a2aA2A 1.0, same token; card at /.well-known/agent-card.json
http://localhost:8080/v1/…REST: the tools, and the application API for admin keys; described at /openapi.json
http://localhost:8080/healthzReadiness, including the database
http://localhost:8080/metricsPrometheus metrics, with Authorization: Bearer ; off without it
  • Metrics count calls by protocol, operation and outcome (slotlock_calls_total, with their duration in slotlock_call_duration_seconds), confirmations, subscriptions, refusals, maintenance and webhook deliveries. No label names a tenant, key or client. createSlotlockMetrics builds them for a server of your own, from its hooks.
  • The server runs as a role that owns nothing and cannot bypass row-level security; a one-off migrate service applies the schema as the owner.
  • SLOTLOCK_AVAILABILITY sets the default bookable hours. A resource can have its own (slotlock hours set, or the dashboard); with neither, no slot is offered. SLOTLOCK_CONFIRM_WRITES=all (as .env.example sets it; the server's default is none) makes every write wait for a person.
  • To serve beyond this machine, put a TLS proxy in front and set SLOTLOCK_PUBLIC_URL.
  • The server limits requests per client address (before any key is looked up), per key, per tenant and for the whole server, and answers 429 rate_limited with Retry-After and the RateLimit fields. The application API counts an admin key's calls in its tenant's own app_read and app_write budgets, apart from the agents'; another key's are its own. A recurring create and every event update (a new title re-expands a recurring event's series too) takes its write token, then waits for a slot: one at a time per tenant (tenant_series_writes) and per server (series_writes); each server process counts its own. A freed slot rests for as long as the server was busy while the write before held it (at most a second), so with one slot, writes that each hold it under a second keep the server busy at most about half the time. It then goes to the waiting tenant with the fewest writes in flight and, among those, the one whose writes have kept the server busy least lately (halving every ten seconds), then to the first to ask. So a tenant sending one write at a time waits for the write running, its rest, and the other tenants with one write in flight whose writes kept the server less busy: many such tenants, new ones included, can each go ahead of it once. Tenants keeping more writes waiting never go ahead of it. Four more of a tenant's writes may wait and the rest get 429. When the server's 64 places are all taken, a write from a tenant with fewer writes in flight than the tenant with the most takes the place of that tenant's last write, which gets 503 server_busy; otherwise it gets 503 at once. An expansion runs on the server's main thread, so any request may also wait for the one running. A write that has waited two seconds gets 429 while its tenant's own write is still running, otherwise 503 server_busy. An application API maintenance run waits the same way for slots of its own: one per tenant, maintenance_runs per server. The health check is not counted. One tenant's requests hold at most tenant_transactions (2) of the server's 10 database connections at once and the rest wait their turn, so a tenant's writes waiting on its own locks leave the others to other tenants and key lookups; a server with one tenant can set tenant_transactions=10. A request whose transaction waits more than 5 seconds for a lock gets 503 server_busy. With more than one server, SLOTLOCK_SHARED_LIMITS=on shares the tenant budgets (tenant_*, app_*) through the database, so N servers allow a tenant its rate once, not N times; each server checks its own buckets first, and goes on with them alone if the database cannot be reached. SLOTLOCK_LIMITS changes the limits; behind a proxy, set SLOTLOCK_TRUSTED_PROXY_HEADER to the field it puts the client's address in (CF-Connecting-IP behind Cloudflare) before the server takes traffic, or every client shares the proxy's budget: the server logs a client_address_field_ignored warning when such a field arrives without the setting. Behind two proxies, such as Cloudflare and then kamal-proxy, name the first one's field: the server reads the last address of a list field such as X-Forwarded-For, which the second proxy writes as Cloudflare's. A client can send that field itself, so let nothing reach the server except through the proxy.
  • The server also bounds what one tenant may store, whichever key, agent or application writes it: 10,000 holds, and 100,000 each of bookings, calendar events and coverage rows, counted as the store's tenantLimits count them, besides 10,000 resources. A write past one answers 409 hold_limit_reached, reservation_limit_reached, event_limit_reached or coverage_limit_reached; SLOTLOCK_LIMITS changes them (tenant_holds, tenant_reservations, tenant_events, tenant_coverage).
slotlock hours set '[{"rrule":"FREQ=WEEKLY;BYDAY=SA","startMinutes":600,"durationMinutes":240}]' --resource vehicle-42

slotlock hours clear --all # back to SLOTLOCK_AVAILABILITY

A rule is rrule, startMinutes and durationMinutes here and in SLOTLOCK_AVAILABILITY. The REST application API spells every field in snake_case, so there it is rrule, start_minutes and duration_minutes. slotlock --help lists every setting.

Booking rules

A resource can also say what a booking on it must satisfy:

slotlock rules set '{"minNoticeMinutes":120,"slotStepMinutes":30,"turnaroundMinutes":30,"enforceHours":true}' --resource vehicle-42

slotlock rules clear --resource vehicle-42

RuleA booking made now
minNoticeMinutesstarts at least this far ahead
maxAdvanceDaysstarts at most this far ahead
slotStepMinutesstarts on a step from local midnight in the resource's time zone
stepOffsetMinutesstarts this far past each step (0 to the step less one): with a 30-minute step, 15 starts bookings on :15 and :45
bufferBeforeMinuteskeeps this long free before it: its leading buffer, which the database enforces
turnaroundMinuteskeeps this long free after it: its trailing buffer, which the database enforces
enforceHourslies inside the resource's bookable hours
checkInMinutesis claimed (checkInReservation) this long after its start, or maintenance releases it
requireCertainlies in time every calendar sync recording coverage for the resource has read within a day
  • Holds, reservations, moves and agents' events are checked when they are made, by the database's clock; a refusal names the rule (booking_rule_violated with rule in the store, booking_rule_min_notice, _max_advance, _slot_step, _outside_hours or _uncertain to an agent). Under requireCertain, a search (the store's and the tools') needs those syncs' coverage too, read within a day, of the slot and its turnaround, so it offers no slot they leave unproven.
  • A series is judged on its first occurrence for notice and advance, and on every occurrence for step and hours. Imported calendar events are not checked: they happened elsewhere.
  • Searches (slotlock_find_next_available, slotlock_find_available_slots, findNextAvailableFor) offer only slots the rules allow, so an offered slot is one the write accepts.
  • Two bookings stay at least the earlier one's trailing buffer plus the later one's leading buffer apart: each occupies from its start less its leading buffer to its end plus its trailing one, and the exclusion constraint keeps those apart. Free/busy reports that time as busy.
  • The REST application API sets them with PUT /v1/resources/{resource_id}/booking-rules, in snake_case; the store with setResourceBookingRules.
  • Defaults: rules for every resource of the tenant (PUT /v1/booking-rules) and for every member of a pool (PUT /v1/pools/{pool}/booking-rules), read back with GET /v1/booking-rules; the store's setBookingRuleDefaults and getBookingRuleDefaults. A resource's rules as they apply (effective_booking_rules, effectiveBookingRules) are the tenant's defaults, overlaid by its pool's, overlaid by its own: a rule set at a narrower level wins. checkInMinutes is a resource's own only. The dashboard's resource page shows which rules a resource inherits, and from where.
  • With no rule set anywhere, nothing changes: every rule is opt-in.
  • A tenant has default rules for at most 1,000 pools (pool_rules_limit_reached, 409, for one more). Merged rules are read leniently: a pool's stepOffsetMinutes under a resource's shorter step applies modulo that step, and a field that is invalid on its own is ignored (and logged), never read as refusing every booking.
Without Docker

With Node.js 22.12+ and PostgreSQL 16 or newer, run each command as npx slotlock after npm install slotlock postgres, or from a clone, built once, as node dist/cli.js (a clone does not put slotlock on your PATH). Use two roles, as Compose does, so two URLs: serve connects as DATABASE_URL, a role that owns nothing and cannot bypass row-level security, and migrate connects as SLOTLOCK_MIGRATE_DATABASE_URL, the schema owner, and grants that role what it needs. serve refuses to start as a superuser or a BYPASSRLS role, which row-level security does not confine, and warns (serving_as_schema_owner) when its role owns the schema.

npm ci && npm run build

Once, as a superuser:

CREATE ROLE slotlock_app LOGIN PASSWORD ' ' NOSUPERUSER NOBYPASSRLS;

export SLOTLOCK_MIGRATE_DATABASE_URL='postgresql:// : @localhost:5432/slotlock' export DATABASE_URL='postgresql://slotlock_app: @localhost:5432/slotlock' export SLOTLOCK_AUTH_TOKEN="$(openssl rand -hex 32)" export SLOTLOCK_CONFIRMATION_SECRET="$(openssl rand -hex 32)" node dist/cli.js migrate node dist/cli.js resource add vehicle-42 --timezone Europe/London node dist/cli.js serve

The slotlock … commands in this README run the same way as node dist/cli.js … (with Compose, docker compose exec slotlock slotlock …).

API

ตัดมาแค่ช่วงต้น อ่านต่อใน repo

ถาม PopNx