@amritk/nish-x86_64-linux 0.21.0 → 0.23.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.
package/std/README.md CHANGED
@@ -24,7 +24,7 @@ whatever program imports it, and subject to the same rules as `examples/` or
24
24
  | [`map.ts`](./map.ts) | `reserve(m, n)` and `getOrInsert(m, k, v)` for the global `Map`. Their bodies are the meaning, and what runs under Node: `reserve` does nothing, and `getOrInsert` is a `get`, and a `set` of `v` when the key was missing. Natively the compiler lowers every call in place — `reserve` to the table's `reserveSlots`, which grows the buckets once so that `n` entries fit without a rebuild, and `getOrInsert` to one `probe` and a `valueAt` or an `insertAt` through its answer — so, like `collections.ts`, it writes no `.ll` of its own ([`docs/wp32-map.md`](../docs/wp32-map.md) §9.2, [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#map-and-set)) |
25
25
  | [`bytes.ts`](./bytes.ts) | `getU16LE` / `getU16BE` to `getF64LE` / `getF64BE` and the `set` twins: `u16`, `u32`, `u64`, `i32`, `i64` and `f64` read from and written into a `u8[]` at an offset, little- or big-endian. Each access is an ordinary index, so out of range is an `index` panic naming the first missing byte. In range the answers are those of the private copies `std/` keeps (`sha256LoadWord`, `chacha20Word`, `poly1305Le64`, sha512's `loadWord` / `storeWord`, `http2FramePut32`, `tlsPutU16`), none of which it replaces. A guard in the caller does not yet remove the checks after inlining. Under Node only the `u16` and `u32` functions answer as natively. Beside them are the runs a parser walks — `skipBlank`, `skipDigits`, `skipJsonText` and `skipJsonPlain`, the first index in a clamped range whose byte is not a JSON blank, a digit, a byte a JSON string holds as itself or a byte a JSON writer copies without an escape — and `copyBytes`, a window copied as one move that panics on a range outside its array. Their bodies call private walkers, which are what runs under Node; natively the compiler replaces each walker's call with a kernel of `runtime/runtime-bytes.c`, or `llvm.memmove` for the copy ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#byte-runs-skipblank-skipdigits-skipjsontext-skipjsonplain-and-copybytes-in-nishbytes)). The runs are variable-time. `tests/link/std_bytes*` are its programs |
26
26
  | [`base64.ts`](./base64.ts) | `nish/base64`: `base64Encode(bytes)`, `base64EncodeInto(src, off, len, dst, at)` and `base64EncodedLength(n)`, padded standard base64 (RFC 4648 §4) for kitty graphics frames. It only encodes, and it indexes its alphabet by the sextet, so it is not constant-time: it is for pixels, not secrets. A strict decoder, and an encoder for key material, are `crypto/base64url.ts`'s `base64StdPadDecode` and `base64StdPadEncode` (`tests/link/std_base64`; the two modules in one program: `tests/link/std_base64_two_modules`) |
27
- | [`random.ts`](./random.ts) | `Rng`, a seeded generator of pseudo-random numbers: `new Rng(seed)`, `nextI32()` and `nextF64()`. A Weyl sequence through all 2^32 states with MurmurHash3's finaliser on each, written with `nish:unsafe`'s `wrappingAdd` and `wrappingMul` so that every answer is the same bits natively and under Node (`tests/link/std_random`; [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#random-numbers-rng-in-nishrandom)). A parallel body or a task that makes its own steps it without a shared write. Not for cryptography |
27
+ | [`random.ts`](./random.ts) | `Rng`, a seeded generator of pseudo-random numbers: `new Rng(seed)`, `nextI32()`, `nextF64()` and `nextInt(lo, hi)`, an unbiased integer in [lo, hi] by rejection sampling. A Weyl sequence through all 2^32 states with MurmurHash3's finaliser on each, written with `nish:unsafe`'s `wrappingAdd`, `wrappingSub` and `wrappingMul` so that every answer is the same bits natively and under Node (`tests/link/std_random`; [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#random-numbers-rng-in-nishrandom)). A parallel body or a task that makes its own steps it without a shared write. Not for cryptography |
28
28
  | [`cli.ts`](./cli.ts) | a command-line tool's `--help`. `HelpMenu` builds it a line at a time — `header`, `usage`, `section`, `row(flag, placeholder, description)`, `note`, `value` — with descriptions lined up at the caller's columns, counted on the plain text, and answers it with `text()`. `Style` paints it in the eight basic ANSI colours, bold and faint, which the terminal's theme defines, or in bold and underline alone when `TERM` declares no colours. `styleFor(fd, agent)` is the policy `nish --help` follows: plain unless the descriptor is a terminal, `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR_FORCE`, `CLICOLOR` and `TERM=dumb` honoured, plain for an agent (`agentDriven`), forced colour always the theme's; it reads the environment and, for the terminal check, forks `/bin/sh` once, which runs `tput` by absolute path. `HelpMenu` and `Style` reach no capability. The compiler's own help is built with it through a relative import, so the module may use only what the last release compiles (`tests/link/std_cli`; [guide](../docs/guide/using-nish/standard-library.md#nishcli)) |
29
29
  | [`sqlite.ts`](./sqlite.ts) | SQLite, vendored in `runtime/sqlite/` and linked into a program that imports the module, through the C adapter `runtime/sqlite/nish-sqlite.c`. `openDatabase(path, flags)` answers a `Database`: `exec`, `begin` / `commit` / `rollback`, `prepare` into a `Statement`, `lastInsertRowid`, `changes` and `close`, which finalizes the connection's statements. A `Statement` binds (`bindI64`, `bindF64`, `bindNull`, `bindText`, `bindTextBytes`, `bindBlob`), steps, resets and reads columns (`columnType`, `columnI64`, `columnF64`, `columnBytes`, `columnText`, and `columnInto`, a copy into the caller's `u8[]`), so a row allocates nothing. Every failure is a `Result` of SQLite's extended code, and `error(code)` builds the `SqliteError` with its message. A handle is an `i32` into the adapter's thread-local tables, and one used after `close` or `finalize` answers `SQLITE_MISUSE`. Reaches `ffi`; links natively and under `--profile wasi` (`tests/link/std_sqlite*`; [`docs/LANGUAGE.md`](../docs/LANGUAGE.md#sqlite-nishsqlite)) |
30
30
  | [`base64.ts`](./base64.ts) | standard base64 (RFC 4648 §4) with `=` padding, encode only: `base64Encode(bytes)` answers a string, `base64EncodeInto(src, off, len, dst, at)` writes into a byte buffer allocating nothing, `base64EncodedLength(n)` sizes it; a range outside its array panics before a byte is written. What kitty graphics frames are sent in; not the constant-time `crypto/base64url` (`tests/link/std_base64`, `std_base64_f64`, `std_base64_negative_*`; [guide](../docs/guide/using-nish/standard-library.md#nishbase64)) |
@@ -81,7 +81,7 @@ is listed in [`SECURITY.md`](../SECURITY.md#threat-model).
81
81
  | [`crypto/hmac.ts`](./crypto/hmac.ts) | `hmacSha256` and `hmacSha384`, the streaming `HmacSha256` and `HmacSha384` (keyed in the constructor, then `update` and `digest`), and `hmacSha256Verify` / `hmacSha384Verify`, which compare a received tag with `timingSafeEqual`. The one-shot functions panic on a message longer than 2^31 − 1 bytes. `HmacSha256Scratch` / `HmacSha384Scratch` are the same tag over hashers the caller allocates once — `begin(key, off, len)`, `update`, `finishInto(out, at)`, `wipe` — so a loop of them inside `using a = arena()` leaves the arena where it was; `hmacCopySha256` / `hmacCopySha384` copy a hasher's state into one the caller holds. `pbkdf2HmacSha256(password, salt, iterations, dkLen)` is PBKDF2 with HMAC-SHA-256, for SCRAM-SHA-256: the password goes in and the key comes out as a `Secret<u8[]>`, every intermediate is wiped, and fewer than one iteration or a `dkLen` below one answers `null` | RFC 2104, RFC 4231 §4, RFC 8018 §5.2 |
82
82
  | [`crypto/hkdf.ts`](./crypto/hkdf.ts) | `hkdfExtractSha256` / `hkdfExtractSha384` (an empty salt is HashLen zeros) and `hkdfExpandSha256` / `hkdfExpandSha384`, which answer `null` for a length below zero or above 255 × HashLen, for an `info` longer than 2^31 − 1 bytes, and for a PRK shorter than HashLen. `hkdfExpandLabelSha256` / `hkdfExpandLabelSha384` are TLS 1.3's HKDF-Expand-Label, which QUIC uses too; the label is given without its `"tls13 "` prefix, and a length outside 0 to 255, a label outside 1 to 249 bytes, a context past 255 bytes or a secret shorter than HashLen panics, since each is the program's choice rather than the peer's. `hkdfExtractInto`, `hkdfExpandInto` (`false` where the others answer `null`) and `hkdfExpandLabelInto` compute the same bytes in an `HkdfScratch` the caller owns, over the hash a length of 32 or 48 names, into the caller's array, and wipe the scratch before they return. A key held as a `Secret<u8[]>` (`nish:secret`) goes through the `*Secret` twins, which read it only inside `exposeWith` and answer a `Secret`: `hkdfSha256Secret` / `hkdfSha384Secret`, each step's, and `hkdfExpandLabelSha256Secret(prk, label, context, length)` / `hkdfExpandLabelSha384Secret`, which panic where HKDF-Expand-Label does. A destination already held as a `Secret` is derived into in place by `hkdfExpandLabelSha256Held(dst, prk, label, context)` / `hkdfExpandLabelSha384Held`, as many bytes as `dst` holds, the plain output copied in inside `exposeWith` and wiped; `dst` may be `prk`. `hkdfExpandLabelHeldInto(hashLength, prk, label, context, out, at, length)` writes a plain window from a held PRK, for output that is plain by design, a record key or IV | RFC 5869 §2, Appendix A; RFC 8448 §3 and RFC 9001 A.1 for the label |
83
83
  | [`crypto/ct.ts`](./crypto/ct.ts) | `timingSafeEqual(a, b)`, which reads every byte whatever it holds, and `timingSafeEqualAt(a, aOff, b, bOff, len)` over two windows, which answers `false` for a window outside its array. Two lengths that differ answer `false` at once, because a length is public, and so does an array longer than 2^31 − 1 bytes | — |
84
- | [`crypto/bcrypt.ts`](./crypto/bcrypt.ts) | `bcryptHash(password, salt, cost)`, the 60-character `$2b$` hash of a `Secret<u8[]>` password under a 16-byte salt and a cost of 4 to 31, and `bcryptVerify(password, hash)`, which reads `$2a$`, `$2b$` and `$2y$` hashes, refuses `$2x$`, and compares in constant time. The first 72 bytes of a password count, up to its first NUL, as under libxcrypt's `crypt_r` (where other implementations differ: [the interoperability table](../docs/security/crypto-bcrypt.md#interoperability)); the Blowfish state is wiped before either returns. `BCRYPT_MAX_PASSWORD`, `BCRYPT_HASH_LENGTH`, `BCRYPT_SALT_SIZE`, `BCRYPT_MIN_COST`, `BCRYPT_MAX_COST`. A `std/threads` task cannot call them yet, since a task may not wipe | OpenBSD `bcrypt(3)`; crypt_blowfish 1.3 for `$2a$` and `$2y$` |
84
+ | [`crypto/bcrypt.ts`](./crypto/bcrypt.ts) | `bcryptHash(password, salt, cost)`, the 60-character `$2b$` hash of a `Secret<u8[]>` password under a 16-byte salt and a cost of 4 to 31, and `bcryptVerify(password, hash)`, which reads `$2a$`, `$2b$` and `$2y$` hashes, refuses `$2x$`, and compares in constant time. The first 72 bytes of a password count, up to its first NUL, as under libxcrypt's `crypt_r` (where other implementations differ: [the interoperability table](../docs/security/crypto-bcrypt.md#interoperability)); the Blowfish state is wiped before either returns. `bcryptHash4(p0, p1, p2, p3, salts, cost)` and `bcryptVerify4(p0, p1, p2, p3, hashes)` do up to four at once, interleaved (four in the time of about 1.2 single hashes on one core), each lane the single path's answer; verify groups lanes by cost, and a malformed lane makes the whole call an `Err` naming it. `BCRYPT_MAX_PASSWORD`, `BCRYPT_HASH_LENGTH`, `BCRYPT_SALT_SIZE`, `BCRYPT_MIN_COST`, `BCRYPT_MAX_COST`. A `std/threads` task may call all four ([BCRYPT-9](../docs/security/crypto-bcrypt.md#findings)) | OpenBSD `bcrypt(3)`; crypt_blowfish 1.3 for `$2a$` and `$2y$` |
85
85
  | [`crypto/base64url.ts`](./crypto/base64url.ts) | `base64urlEncode(data)` and `base64urlDecode(text)`, unpadded; `base64StdPadEncode(data)` and `base64StdPadDecode(text)`, the padded standard alphabet that SCRAM and PEM use, constant-time in both directions. These are not `nish/base64`, which encodes only, is not constant-time and is for pixels; the names differ because std modules share the program's one namespace. Decoding is strict, so every byte string has one spelling. `base64urlDecode` answers `null` for any `=`, a character outside the alphabet, a length of 1 mod 4 or nonzero unused low bits; `base64StdPadDecode` requires the padding, answering `null` unless the text is whole four-character groups with `=` only as the last one or two characters, every other character in the alphabet and the unused bits zero. Both refuse a text longer than 2^31 − 1 bytes, and `base64urlEncode` panics on such an array | RFC 4648 §4, §5, §10 (`tests/link/std_base64_padded`) |
86
86
  | [`crypto/sha1.ts`](./crypto/sha1.ts) | `sha1(data)`, a fresh 20-byte digest, and `SHA1_SIZE` and `SHA1_BLOCK`. **Not for security**: SHA-1 is broken for collisions, and it is here only because RFC 6455 §4.2.2's `Sec-WebSocket-Accept` is defined on it (`nish/net/websocket` is its importer). One-shot only; it panics on an array longer than 2^31 − 1 bytes. It came after the #363 audit and has no record in `docs/security/` | FIPS 180-4 §6.1 |
87
87
  | [`crypto/x25519.ts`](./crypto/x25519.ts) | `x25519(scalar, u)`, the shared secret as a `Secret<u8[]>`, and `x25519Base(scalar)`, the public key, each from a `Secret<u8[]>` scalar, on ten 25.5-bit limbs in `i64`. Either answers `null` unless its arguments are `X25519_SIZE` (32) bytes; the scalar is clamped on a copy, which is wiped | RFC 7748 §5.2, §6.1, Wycheproof `x25519_test` |
@@ -202,7 +202,7 @@ reads one, and it still takes its randomness as an argument.
202
202
  | [`net/http3-server.ts`](./net/http3-server.ts) | `Http3Server`: HTTP/3 over QUIC on one UDP socket for a program that owns its loop — a pool of `QuicConnection` slots with an `Http3Connection` each, a slot's built on the first Initial accepted into it, slots 0 up, and reused, while the connection-ID index, the timer wheel and the rings are sized from the cap at start-up; a `QuicListener` in front; datagrams routed by a hashed index of every slot's connection IDs; `receive(now, key)`, `tick(now)`, `ready()` for the next slot with news, `flush(now)` sending paced GSO flights, `timeout(now)` from a timer wheel. A handshake that did not choose ALPN `h3` is closed with no_application_protocol (RFC 9114 §3.1); a slot done after GOAWAY is closed and freed. Every slot's final responses carry the server's one `HttpDate` (`date`), refreshed at `connection(slot)` and `ready()` and formatted at most once a second. `stop()` releases every connection and the listener and wipes the configuration's stateless reset and Retry token keys, which are the server's once handed in, and its last answer; the keys are zeroed in place, so a `QuicServerConfig` serves one server (QUIC-5). Native only, as `nish:net` is | A Nish client across loopback (`tests/link/net_http3_server`): requests, a paced 300 KB response, GOAWAY and the slot freed, ALPN, a full pool, the arena over connections through one slot while the pinned date moves a second on for each, one date formatted for two slots; every key read back as zero after `stop()` (`tests/link/net_quic_server_wipes`); aioquic and `curl --http3` against its `serve` mode |
203
203
  | [`net/webtransport.ts`](./net/webtransport.ts) | `WebTransport`, the server side of WebTransport over HTTP/3 as draft-ietf-webtrans-http3-02 has it and Chrome and `wtransport` 0.7 negotiate it, a layer over one `Http3Connection` whose `Http3Config.webtransportSessions` turns it on (extended CONNECT, SETTINGS_H3_DATAGRAM and the draft's two WebTransport settings): `next()` answers `WT_SESSION` (the program `accept`s or `refuse`s it), `WT_DATAGRAM`, `WT_STREAM`, `WT_STREAM_DATA`, `WT_STREAM_END`, `WT_STREAM_RESET`, `WT_STREAM_STOPPED`, `WT_WRITABLE`, `WT_DRAIN` and `WT_CLOSED`, and passes a plain request's `H3_*` events through; `sendDatagram` (the quarter stream ID, then the payload), `openStream`, `write`, `resetStream`, `stopSending`, `close` (CLOSE_WEBTRANSPORT_SESSION) and `drain`. Streams that arrive before their session wait, bounded; every cap is fixed at start-up, and a warm session allocates nothing per datagram or stream (`docs/security/webtransport.md`) | RFC 9220, RFC 9297 and draft-02: a Nish client over QUIC — sessions, datagrams at the size limits, streams both ways, close and drain from either side, every refusal — and one exchange recorded from a `wtransport` 0.7 client replayed byte for byte (`tests/link/net_webtransport`) |
204
204
  | [`net/router.ts`](./net/router.ts) | `Router`, a method and path router that knows no carrier and allocates nothing per match: `add(method, pattern)` answers the route's index (0, 1, 2 in the order added) or a refusal — `ROUTER_BAD_PATTERN`, `ROUTER_BAD_METHOD`, `ROUTER_TOO_LONG`, `ROUTER_AMBIGUOUS` (`*` not last), `ROUTER_DUPLICATE` (the same shape and method, whatever the parameter names) or `ROUTER_FULL`, against `RouterLimits`; `match(method, mStart, mEnd, target, tStart, tEnd, params)` answers the index, `ROUTER_NOT_FOUND` or `ROUTER_METHOD_NOT_ALLOWED`, writing each `:name` and a final `*` as a `[start, end)` pair into the caller's `params` (`ROUTER_TOO_MANY_PARAMS` when it is short). Literal before parameter before `*`, segment by segment, backtracking; a lookup enters each node at most once. The query is left unparsed and nothing is decoded: `routerDecode` decodes one parameter (`ROUTER_BAD_ESCAPE`, `ROUTER_NO_ROOM`) (`docs/security/router.md`) | Every refusal code, precedence and backtracking, 405 against 404, `%2F` kept in one segment, the query, `*` and authority-form targets, a target deeper than any route, the lookup bound, params past storage, and the arena flat over matches (`tests/link/net_router`, `net_router_f64`, five `net_router_negative_*`) |
205
- | [`net/postgres.ts`](./net/postgres.ts) | `PgConnection`, a PostgreSQL client that reads no socket: the program fills `inputBuffer()` and sends `outputBuffer()`, and `next()` answers `PG_NEED_MORE`, `PG_READY`, `PG_DESCRIBED`, `PG_ROW`, `PG_COMPLETE`, `PG_ERROR`, `PG_NOTICE`, `PG_GROUP_DONE` or the final `PG_FAILED` (`failure`, one of the `PG_ERR_*` codes). Protocol 3.0: startup, `trust` and SCRAM-SHA-256 with the server's signature checked (cleartext and MD5 refused), the extended query protocol — `prepare`, `bindStart`, `paramInt` / `paramText` / `paramBytes` / `paramTextArray` / `paramNull`, `bindEnd`, `closeStatement`, `sync` — with up to `maxInFlight` groups pipelined. A row's columns are windows onto the receive buffer (`colStart`, `colLength`), read in place by `int`, `bool`, `isNull` and `PgText`'s decoders (`timestamptz`, `PgArrayWalker` for `text[]`); no per-row or per-column allocation. The password is a `Secret<u8[]>`, wiped once the proof is made. Caps on message size, columns, groups in flight and SCRAM iterations (`docs/security/postgres.md`) | PostgreSQL 18's frontend/backend protocol chapter, RFC 5802 and RFC 7677: every builder, event and refusal (`tests/link/std_postgres`), and exchanges recorded from PostgreSQL 18 by `tests/postgres/record.mjs` replayed byte for byte (`tests/link/std_postgres_replay`) |
205
+ | [`net/postgres.ts`](./net/postgres.ts) | `PgConnection`, a PostgreSQL client that reads no socket: the program fills `inputBuffer()` and sends `outputBuffer()`, and `next()` answers `PG_NEED_MORE`, `PG_READY`, `PG_DESCRIBED`, `PG_ROW`, `PG_COMPLETE`, `PG_ERROR`, `PG_NOTICE`, `PG_GROUP_DONE` or the final `PG_FAILED` (`failure`, one of the `PG_ERR_*` codes). Protocol 3.0: startup, `trust` and SCRAM-SHA-256 with the server's signature checked (cleartext and MD5 refused), the extended query protocol — `prepare`, `bindStart`, `paramInt` / `paramText` / `paramBytes` / `paramTextArray` / `paramIntArray` / `paramNull`, an array built element by element into the Bind (`arrayStart`, `arrayElement`, `arrayInt`, `arrayNull`, `arrayEnd`), `bindEnd`, `bindCancel` to drop a Bind not yet ended, `closeStatement`, `sync` — with up to `maxInFlight` groups pipelined. `PgBatcher` queues keyed reads of one statement shape (`addInt`, `addText`) and `take(conn)` binds up to `maxRequests` of them, each key once, as one execution over `unnest($1) with ordinality`; `row`, `end` and `fail` route its rows by their server-supplied ordinal, checked first, to `next(run)`'s per-request `PG_BATCH_ROW` and `PG_BATCH_DONE` / `PG_BATCH_MISSING` / `PG_BATCH_FAILED`; `take` answers a fresh handle each time, so a server's late or repeated event on a spent one is counted in `stray`, never routed or panicked on. It reads no clock and allocates nothing per request or row. A row's columns are windows onto the receive buffer (`colStart`, `colLength`), read in place by `int`, `bool`, `isNull` and `PgText`'s decoders (`timestamptz`, `PgArrayWalker` for `text[]`); no per-row or per-column allocation. The password is a `Secret<u8[]>`, wiped once the proof is made. Caps on message size, columns, groups in flight and SCRAM iterations (`docs/security/postgres.md`) | PostgreSQL 18's frontend/backend protocol chapter, RFC 5802 and RFC 7677: every builder, event and refusal (`tests/link/std_postgres`), and exchanges recorded from PostgreSQL 18 by `tests/postgres/record.mjs` replayed byte for byte (`tests/link/std_postgres_replay`); the batcher's queueing, caps and ordinal refusals (`tests/link/std_postgres_batch`) |
206
206
 
207
207
  **How a carrier drives `TlsServer`.** The server speaks in handshake
208
208
  *messages*, tagged by level: `TLS_LEVEL_INITIAL` (cleartext — ClientHello,
package/std/cli.ts CHANGED
@@ -273,9 +273,25 @@ export class HelpMenu {
273
273
 
274
274
  /** The opening line, the program's name, `version` and a one-line `summary`, and a blank after it. */
275
275
  header(version: string, summary: string): void {
276
+ this.title(version, summary)
277
+ this.lines.push("")
278
+ }
279
+
280
+ /**
281
+ * The opening line alone, without `header`'s blank after it, for a menu that
282
+ * puts something under its title (`line`) before the blank.
283
+ */
284
+ title(version: string, summary: string): void {
276
285
  const style = this.style
277
286
  this.lines.push(`${style.name(this.program)} ${style.dim(version)} ${style.dim(summary)}`)
278
- this.lines.push("")
287
+ }
288
+
289
+ /**
290
+ * A line exactly as given, painted (or not) by the caller with the menu's
291
+ * `style`: a rule, a banner, a blank. Its width is the caller's to keep.
292
+ */
293
+ line(text: string): void {
294
+ this.lines.push(text)
279
295
  }
280
296
 
281
297
  /**