@rsc-kit/mcp 0.26.0 → 0.27.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.
package/dist/recipes.js CHANGED
@@ -1730,6 +1730,39 @@ validates at runtime), move info/servers/security to the option, move a
1730
1730
  route's summary/tags/responses to its openapi export.
1731
1731
 
1732
1732
  Full guide: read_guide({ slug: 'openapi' }).`,
1733
+ },
1734
+ {
1735
+ topic: 'secrets',
1736
+ summary: 'The secrets rsc-kit reads - RSC_HOST_CALL_SECRET, RSC_SIGNING_SECRET, RSC_ACTION_ENCRYPTION_KEY - what each protects, how to generate them, where they go in production, and what rotating one does',
1737
+ body: `Three, each its own key for its own job. Never reuse one as another.
1738
+
1739
+ RSC_HOST_CALL_SECRET renderer <-> Go/Laravel backend calls. Required with a
1740
+ backend; read by the renderer AND the backend.
1741
+ RSC_SIGNING_SECRET signs refreshOn names a page hands the tab. Required in
1742
+ production when any page/section uses refreshOn (the
1743
+ server refuses to serve without it). Renderer only.
1744
+ Never falls back to the host-call secret.
1745
+ RSC_ACTION_ENCRYPTION_KEY optional: pins the key server actions encrypt with, so
1746
+ actions survive a deploy. Set at BUILD time too.
1747
+
1748
+ GENERATE - once each, a different value each:
1749
+ openssl rand -base64 32
1750
+ node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
1751
+ php -r "echo base64_encode(random_bytes(32)), PHP_EOL;"
1752
+ Development: create-rsc-kit / rsc-kit init (with a backend) and
1753
+ php artisan rsc:install write the first two into .env already.
1754
+
1755
+ PRODUCTION - in the platform's environment settings, never in git:
1756
+ - the SAME value on every instance (a tab served by one renderer is checked by
1757
+ another; a call may land on any backend replica);
1758
+ - read at runtime, so one build deploys anywhere (except the action key).
1759
+
1760
+ ROTATING is a deploy:
1761
+ - host-call: change renderer and backend together; between them every rpc() is refused.
1762
+ - signing: open pages stop refreshing until reloaded; nothing else breaks.
1763
+ - action key: actions from pages loaded before the change fail.
1764
+
1765
+ Docs: /hosts/deployment#secrets.`,
1733
1766
  },
1734
1767
  {
1735
1768
  topic: 'env',
@@ -1792,6 +1825,65 @@ SAY IT CHANGED - wherever the change happens, not only in an action:
1792
1825
  Go: reg.Changed(ctx, "team:"+teamID+":repos") // the webhook handler, a job
1793
1826
  Laravel: Rsc::changed("team:$teamId:repos"); // a controller, a job, a listener
1794
1827
  JS only: import { changed } from '@rsc-kit/core/changed'; await changed(\`team:\${team}:repos\`)
1828
+ ANY DATABASE - rsc-kit ships no driver; hand it yours. Postgres:
1829
+ postgresVersions(sql) (postgres.js/Bun.sql). MySQL/MariaDB: sqlVersions({
1830
+ query: async (t, p) => (await pool.query(t, p))[0] }) (mysql2). SQLite:
1831
+ sqlVersions({ query: async (t, p) => db.query(t).all(...p) }). libSQL/Turso:
1832
+ sqlVersions({ query: async (sql, args) => (await client.execute({ sql, args })).rows }).
1833
+ Redis/ORM query builders: createVersions({ read, bump, listen?, notify? }).
1834
+ Instant across instances: postgres.js LISTEN, Redis pub/sub; others within ~1s.
1835
+ NO BACKEND (JS only) - changed() works as-is on one server. With more than
1836
+ one instance, or a worker process that finishes jobs, keep versions in a
1837
+ table they all share: installVersionSource(postgresVersions(sql)) in
1838
+ instrumentation.ts register() AND in the worker (postgres.js LISTENs, so
1839
+ cross-instance changes are instant; Bun.sql polls once a second; any SQL
1840
+ driver/ORM: sqlVersions({ query }) - Prisma: query: (t, p) =>
1841
+ prisma.$queryRawUnsafe(t, ...p); anything else (Drizzle query builder, Redis,
1842
+ KV): createVersions({ read(names), bump(names), listen?, notify? }) and
1843
+ rsc-kit does the waiting/waking). An app-installed store always wins over a
1844
+ backend's. In-memory versions fail with 2+ instances; in production rsc-kit logs a
1845
+ warning once when it falls back to them - installVersionSource(memoryVersions())
1846
+ says "one instance on purpose" and silences it.
1847
+ Table: rsc_versions(name TEXT PRIMARY KEY, version BIGINT NOT NULL) - same as
1848
+ Go's SQLVersions. ANY process can bump without rsc-kit: upsert version + 1
1849
+ (Postgres: ON CONFLICT (name) DO UPDATE SET version = rsc_versions.version + 1;
1850
+ then NOTIFY rsc_versions).
1851
+ TABLE - the app owns rsc_versions (rsc-kit never creates/alters it at
1852
+ runtime): name TEXT PRIMARY KEY (MySQL: VARCHAR(255)), version BIGINT NOT
1853
+ NULL. Extra columns fine if they have defaults. Drizzle: a pgTable in your
1854
+ schema + createVersions with the query builder (not raw SQL).
1855
+ BUN / no LISTEN - keep Bun.sql (or Drizzle over it) for read/bump and open one
1856
+ postgres.js connection ({ max: 1 }) only for listen/notify in createVersions:
1857
+ instant across instances.
1858
+ AFTER COMMIT - call changed() AFTER the transaction resolves, never inside
1859
+ it: a tab refreshes at once and would read the old data, then sit at the new
1860
+ version. Laravel: DB::afterCommit(fn () => Rsc::changed(...)).
1861
+ SAME NAME - many sections/pages may refresh on one name; one changed() moves
1862
+ it once and every tab refreshes what it shows. Section names are per page.
1863
+ STARTUP - assertSigningSecret() from @rsc-kit/core/changed in
1864
+ instrumentation.ts register() makes a deploy without the secret fail before
1865
+ taking traffic (Kubernetes readiness); otherwise it is refused on the first
1866
+ request.
1867
+ SECRET - set RSC_SIGNING_SECRET, the same on every instance, with or without
1868
+ a backend (NOT the host-call secret). In production an app using refreshOn
1869
+ refuses to serve without it. Names are signed per request, never at build.
1870
+ REQUEST - a refreshOn function runs per request: cookies()/headers() work,
1871
+ e.g. a name for the signed-in user.
1872
+ SHARED - section(name, C, { refreshOn, shared: true }) for a section that
1873
+ renders the same for everyone allowed to see the page: tabs refreshing
1874
+ because of the same change share ONE render (guards still run per tab).
1875
+ Never on per-visitor content - rpc() runs with the first visitor's session.
1876
+ LARAVEL AT SCALE - RSC_VERSIONS=database (publish rsc-migrations) and
1877
+ installVersionSource(postgresVersions(sql)) in the renderer: watching costs
1878
+ PHP zero requests; Postgres NOTIFY makes it instant.
1879
+ DEBUG - dev console lists what each region watches; a region missing there
1880
+ rendered no names (server log says why). Hidden tabs stop watching and catch
1881
+ up when shown.
1882
+ COST - per visible tab one SSE connection (keepalive every 8s - set
1883
+ RSC_STREAM_KEEPALIVE_MS lower if a server/proxy drops idle connections sooner;
1884
+ ~40 bytes
1885
+ per change); per change one refresh of just the sections showing the name;
1886
+ per server one versions read covering all its tabs.
1795
1887
  HOW - a name has a version, a number that moves when it is said to have
1796
1888
  changed; nothing else travels. The page learns versions at render, one SSE
1797
1889
  stream per tab (only on a page with names) watches them, and a moved version