Skip to content

Commit 38cab6b

Browse files
authored
docs: add 0.6 migration guide, link from README (#83)
1 parent 44ead78 commit 38cab6b

4 files changed

Lines changed: 109 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Framework-neutral foundation for building devframes.
2222
<b>Documentation:</b> <a href="https://devfra.me/">https://devfra.me/</a>
2323
</p>
2424

25+
<p align="center">
26+
Upgrading from 0.5.x? See the <a href="https://devfra.me/guide/migration-0.6">0.6 migration guide</a> (source: <a href="./docs/guide/migration-0.6.md">docs/guide/migration-0.6.md</a>).
27+
</p>
28+
2529
## Sponsors
2630

2731
<p align="center">

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function guideItems(prefix: string): DefaultTheme.NavItemWithLink[] {
3333
{ text: 'Hub (multi-tool)', link: `${prefix}/guide/hub` },
3434
{ text: 'Client Scripts & Context', link: `${prefix}/guide/client-context` },
3535
{ text: 'Agent-Native (experimental)', link: `${prefix}/guide/agent-native` },
36+
{ text: 'Migrating to 0.6', link: `${prefix}/guide/migration-0.6` },
3637
]
3738
}
3839

docs/guide/migration-0.6.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Migrating to 0.6
6+
7+
0.6 tightens `defineDevframe`'s metadata contract, replaces the terminal and WebSocket transports, and ships a ready-made authentication layer with real enforcement. This page covers every breaking change between 0.5.x and 0.6 and how to move past each one.
8+
9+
## `defineDevframe` requires four more fields
10+
11+
`version`, `packageName`, `homepage`, and `description` are now required alongside `id` and `name`. Source them from your own `package.json` so they stay in sync with what you publish:
12+
13+
```ts
14+
import pkg from '../package.json' with { type: 'json' }
15+
16+
export default defineDevframe({
17+
id: 'my-devframe',
18+
name: 'My Devframe', // display label
19+
version: pkg.version,
20+
packageName: pkg.name, // maps from package.json's `name`
21+
homepage: pkg.homepage,
22+
description: pkg.description,
23+
setup(ctx) { /**/ },
24+
})
25+
```
26+
27+
See [Devframe Definition](./devframe-definition#sourcing-metadata-from-package-json) for the full field reference, including the new optional `duplicationStrategy`.
28+
29+
## Auth handshake methods are renamed
30+
31+
The two pre-trust RPC methods moved under the `anonymous:` prefix so `isAnonymousRpcMethod` (the rule an authorization gate checks) covers them without a separate allowlist:
32+
33+
| 0.5.x | 0.6 |
34+
|-------|-----|
35+
| `devframe:anonymous:auth` | `anonymous:devframe:auth` |
36+
| `devframe:auth:exchange` | `anonymous:devframe:auth:exchange` |
37+
38+
Calling through the client API (`rpc.requestTrustWithToken()`, `rpc.requestTrustWithCode()`) needs no change — only a custom node-side handler that registered these method names directly, or an `authorize` gate that pattern-matched on the old names, needs updating.
39+
40+
## WebSocket connections now enforce origin and (optionally) trust
41+
42+
Two independent gates landed on the RPC socket:
43+
44+
- **Cross-origin upgrades are rejected by default.** Only loopback origins (`localhost`/`127.0.0.1`/`::1`) and requests with no `Origin` header (native, non-browser clients) are accepted. Reaching the tool from another host — a LAN address, a tunnel, a reverse proxy — needs an explicit allowlist:
45+
46+
```ts
47+
await startHttpAndWs({
48+
context: ctx,
49+
port: 9999,
50+
allowedOrigins: ['https://my-tunnel.example.com'],
51+
})
52+
```
53+
54+
Pass `allowedOrigins: false` to disable the check entirely (not recommended).
55+
56+
- **Real authorization enforcement is now available, opt-in.** `auth: true` (the default) keeps 0.5.x's behavior — every registered method stays callable regardless of trust. Passing a `DevframeAuthHandler` instead turns on the gate described in [Security](./security#the-pre-trust-gate): an untrusted caller can only reach `anonymous:`-prefixed methods, and everything else throws [`DF0036`](../errors/DF0036). The [Interactive Auth](/helpers/interactive-auth) recipe (`createInteractiveAuth`) builds one of these for you — a code/link banner, the handshake handlers, and the connect-time trust hook — so a host doesn't reimplement the protocol:
57+
58+
```ts
59+
import { startHttpAndWs } from 'devframe/node'
60+
import { createInteractiveAuth } from 'devframe/recipes/interactive-auth'
61+
62+
const auth = createInteractiveAuth(ctx)
63+
const server = await startHttpAndWs({ context: ctx, port: 9999, auth })
64+
auth.printBanner()
65+
```
66+
67+
## Terminals run on `zigpty`, not the `node-pty` peer
68+
69+
Interactive PTY sessions (`ctx.terminals.startPtySession()`) now spawn through [`zigpty`](https://github.com/pithings/zigpty)'s prebuilt native bindings, bundled with `@devframes/hub` — there's no more optional `node-pty` peer dependency to install. Drop it from your own `package.json` if you added it for terminal support; where `zigpty`'s bindings can't load for a platform, sessions degrade to pipe-based emulation automatically.
70+
71+
## `StartedServer.wss` is now `StartedServer.ws`
72+
73+
The RPC socket transport moved from `ws` to [`crossws`](https://crossws.unjs.io/), so the handle `startHttpAndWs`/`createDevServer` return changed shape:
74+
75+
```ts
76+
// 0.5.x
77+
const server = await startHttpAndWs({ context: ctx, port: 9999 })
78+
server.wss.clients // ws.WebSocketServer
79+
80+
// 0.6
81+
const server = await startHttpAndWs({ context: ctx, port: 9999 })
82+
server.ws // crossws NodeAdapter
83+
```
84+
85+
If you only read `server.close()`, `.origin`, `.port`, or `.rpcGroup`, nothing else changes. Code that reached into `.wss` for the raw `ws` server needs the equivalent `crossws` `NodeAdapter` API instead.
86+
87+
## `devframe/utils/human-id` is gone
88+
89+
The human-readable ID generator was removed with no direct replacement. Use `devframe/utils/nanoid` for a short random ID, or `devframe/utils/crypto-token`'s `randomToken()` / `randomDigits()` for anything security-sensitive (bearer tokens, one-time codes):
90+
91+
```ts
92+
// 0.5.x
93+
import { humanId } from 'devframe/utils/human-id'
94+
95+
humanId() // 'bright-orange-tiger'
96+
```
97+
98+
```ts
99+
// 0.6
100+
import { nanoid } from 'devframe/utils/nanoid'
101+
102+
nanoid() // short URL-safe ID, no word-list dependency
103+
```

docs/guide/standalone-cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ program
340340
await program.parseAsync()
341341
```
342342

343-
`createDevServer` returns the underlying `StartedServer` handle (`origin`, `port`, `app`, `wss`, `rpcGroup`, `close()`) so the surrounding program can drive graceful shutdown — SIGINT, hot reload, integration tests.
343+
`createDevServer` returns the underlying `StartedServer` handle (`origin`, `port`, `app`, `ws`, `rpcGroup`, `connectionMeta()`, `close()`) so the surrounding program can drive graceful shutdown — SIGINT, hot reload, integration tests.
344344

345345
For typed flag schemas, `parseCliFlags(schema, rawBag)` (from `devframe/adapters/cli`) validates a commander/yargs flag bag against a `CliFlagsSchema` (the same `defineCliFlags(...)` value you'd put on `cli.flags`). Typed-schema validation works with any CLI framework.
346346

0 commit comments

Comments
 (0)