← Документация · nv-lang/nova-tls

nova-tls

TLS-клиент/сервер для Nova — рукопожатие клиента и сервера, режимы верификации сертификата (системные корни / кастомный CA / SPKI-пиннинг / insecure-skip-verify для тестов), взаимный TLS (mTLS) и соответствие io.Read/io.Write поверх std.net.TcpStream.

Бэкенд: mbedTLS через тонкую C-прослойку (native/tls_c_shim.c) — чистый C, для сборки этого пакета Rust/cargo не нужен. mbedTLS v3.6.2 завендорен в native/mbedtls/ (план 193 Ф.2 gate-3, 2026-07-12 — см. native/mbedtls/VENDORED.md) и собирает себя сам при первом nova test/nova build через общий механизм сборки-с-кэшированием [ffi] vendor_src_dirs — ручная установка через vcpkg/system не нужна (вручную положенная предсобранная библиотека под native/lib/ тоже работает как есть, минуя vendor-сборку).

Извлечено из монорепозитория Nova, из std/tls (базовый дизайн — план 116 + план 195, замена бэкенда на mbedTLS), в отдельный репозиторий по плану 193 — эталонный образец паттерна нативных модулей Nova (план 195: фасад .nv + C-прослойка + предсобранная .lib, подключено через [ffi], без Rust). Публичный API не изменился по сравнению с std.tls — сдвинулся только путь модуля (std.tls.* -> tls.*; см. заметку «Путь модуля» ниже).

Использование

import tls.{TlsStream, ClientConfig, VerificationMode}
import std.net.{Net, TcpStream, SocketAddr, real_net}

fn fetch(host str, port u16) Net -> Result[(), TlsError] {
    ro tcp = TcpStream.connect(SocketAddr.new(host, port))!!
    ro cfg = ClientConfig.new(host)
    consume stream = TlsStream.connect(tcp, cfg)!!
    stream.write_all("GET / HTTP/1.1\r\nHost: ${host}\r\n\r\n".bytes())!!
    ro resp = stream.read_to_vec(4096)!!
    stream.close()
    Ok(())
}

Структура

nova-tls/
├── nova.toml            [package] name = "tls"; [lib] src = "src"; [ffi] native shim
├── native/
│   ├── tls_c_shim.c      mbedTLS backend (compiled via [ffi] c_shims)
│   ├── tls_shim.h         C-side prototypes (Nova <-> C ABI contract)
│   └── tls_mozilla_roots.h  compiled-in Mozilla root CA bundle
└── src/
    ├── ffi.nv             extern "C" fn declarations against native/tls_c_shim.c
    ├── error.nv            TlsError (typed error surface)
    ├── config.nv           ClientConfig / ServerConfig / VerificationMode / ClientCertMode
    ├── client.nv           TlsStream.connect (client handshake)
    ├── server.nv           TlsStream.accept (server handshake)
    ├── stream.nv           TlsStream (sans-I/O pump + io.Read/io.Write conformance)
    ├── *_test.nv           peer tests (same-module, positive)
    ├── neg/                EXPECT_COMPILE_ERROR fixtures (standalone CUs)
    └── testdata/           self-signed fixture certs (server + mTLS client CA/leaf)

Путь модуля

D78 rev-4 (root peers, spec/decisions/07-modules.md «Root peers — .nv-файлы прямо в source root») позволяет .nv-файлам, лежащим прямо в исходном корне пакета (src/, согласно [lib] src выше), объявлять однокомпонентную форму module <package_name> — группу пиров, аналогичную lib.rs в Cargo. Вся поверхность этого пакета лежит прямо в src/ (src/{client,server,stream,...}.nv, все объявляют module tls), поэтому итоговый модуль — это голое имя пакета, tls — без «статтера». Импортировать как import tls.{TlsStream, ...}, как из [dependencies]-потребителя другого пакета, так и из независимого файла того же пакета (например, src/neg/*.nv использует import tls.{ClientConfig} для доступа к этим пирам).

До амендмента rev-4 этот пакет жил по пути src/tls/*.nv по старому правилу rev-3 parent_dir.target, которое заставляло доменную папку повторять имя пакета (module tls.tls, импорт tls.tls.{...}) — перенесён на root peers 2026-07-13 (план 202 Ф.3).

Автономная сборка

Нужен тулчейн Nova (CLI nova + clang). Rust/cargo не нужен, ручная установка mbedTLS не нужна — mbedTLS собирает себя сам из завендоренных исходников в native/mbedtls/ при первом запуске (план 193 Ф.2 gate-3; можно положить предсобранные mbedtls/mbedx509/mbedcrypto под native/lib/ вместо этого, если хотите пропустить эту разовую сборку, например vcpkg install mbedtls).

# Boehm GC (mandatory Nova runtime dep) needs its own lib/include dirs —
# point NOVA_GC_LIB_DIR (+ optional NOVA_GC_INCLUDE_DIR) at a prebuilt
# bdwgc if it isn't reachable via the default vcpkg/system lookup
# (see compiler-codegen/src/test_runner.rs detect_boehm).
#
# `nova` does not (yet) bundle/locate the standard library relative to the
# nova.exe install — a standalone package must point it at a Nova checkout's
# std/ via NOVA_STD_PATH (compiler-codegen/src/manifest.rs resolve_std_path):
export NOVA_STD_PATH=/path/to/nova/std

# Ditto for the compiler's own C runtime (compiler-codegen/nova_rt/ + the
# libuv submodule it needs) — NOVA_CG_INCLUDE / NOVA_RT_DIR, symmetric with
# NOVA_STD_PATH above (resolve_paths in nova-cli/src/main.rs; this is what
# closed the Plan 193 Ф.1 blocker previously documented here — no more need
# to vendor the compiler's runtime into this repo):
export NOVA_CG_INCLUDE=/path/to/nova/compiler-codegen
export NOVA_RT_DIR=/path/to/nova/compiler-codegen/nova_rt

# Use `nova test`, not `nova build <single-file>`, for anything beyond a
# syntax/import smoke check — this package has no `main`, and isolated
# single-file builds of a library CU can hit generic-inference ambiguities
# that a full test CU resolves via its own call sites (verified upstream:
# the same `ffi.nv` hits it identically inside the Nova monorepo).
nova test src

Лицензия

Двойная лицензия — MIT или Apache-2.0, на ваш выбор — те же условия, что у компилятора Nova и стандартной библиотеки.