v0.7.0 4c16dd8

mux.go RawHistoryFolder

1// Private daemonmux QUIC carriage.
2//
3// This file exposes the explicit, mux-specific raw QUIC carriage the daemonmux
4// multiplexer (internal/adapters/daemonmux) runs over. It introduces no
5// transport and no second authentication scheme: it reuses the authenticated
6// bootstrap contracts unchanged - the ephemeral certificate and the exact
7// SHA-256 pin, the single-use 32-byte token, and the single-use nonce - and
8// hands daemonmux the connection's one bidirectional stream as a raw bounded
9// transport (wire.BoundedTransport).
10//
11// One QUIC connection is one daemonmux physical connection and therefore
12// exactly one bidirectional stream: logical daemonmux attachments are
13// multiplexed inside that stream and are never mapped to native QUIC streams.
14// The one-stream guard stays in force for the whole connection lifetime, and
15// the bootstrap credential is consumed exactly once, so one freshly minted
16// Server is the daemon half of exactly one physical connection and
17// DialMuxContext is the broker half of one.
18//
19// The caller-supplied setup context (or its deadline) bounds the whole setup:
20// the credential's wait and expiry, the pinned TLS dial, the bounded auth
21// record exchange, and the daemonmux physical handshake the caller runs
22// afterwards over the returned carriage. A rejected pin, token, or nonce
23// closes the carriage and hands nothing out. A successful carriage detaches
24// from the setup context in the daemonmux EndpointConnector, which owns the
25// physical lifetime from then on.
26//
27// The one-time token and nonce stay in the caller-held Readiness record, never
28// in the dial address: the address is composed by the caller from the resolved
29// target host plus the readiness port, exactly like the existing bootstrap
30// dial. Authorization material is absent from addresses, logs, and errors.
31
32package quic
33
34import (
35	"context"
36	"errors"
37	"time"
38
39	"github.com/bnema/vev/internal/protocol/wire"
40)
41
42// ErrMuxCarriage reports a QUIC setup that produced no raw bounded carriage, or
43// an AcceptMux call on a nil Server. The QUIC transport always implements
44// wire.BoundedTransport, so this is a local construction fault, not a peer
45// outcome; a transport that somehow lacks the capability is closed rather than
46// handed to daemonmux unbounded.
47var ErrMuxCarriage = errors.New("quic: mux carriage is not a bounded transport")
48
49// DialMuxContext establishes one authenticated raw bounded daemonmux carriage.
50// It reuses the bootstrap dial contract unchanged: the TLS handshake pins the
51// ephemeral server's exact SHA-256 certificate fingerprint, and exactly one
52// bounded token-plus-nonce auth record travels inside the encrypted single
53// stream. Nothing is handed out unless the pin matched and the record was sent;
54// a failed pin, refused record, or expired credential closes the carriage and
55// returns an error that carries no bootstrap secret.
56//
57// addr is composed by the caller from the resolved target host and the
58// readiness port. ctx (or timeout, whichever ends first) bounds resolution, the
59// pinned dial, and the auth exchange together; the daemonmux physical handshake
60// that follows shares the caller's setup context through
61// EndpointConnector.Connect, so one setup deadline covers bootstrap, dial,
62// auth, and mux handshake.
63//
64// The returned carriage owns exactly one bidirectional QUIC stream and
65// satisfies the raw framed carriage daemonmux consumes (Send, RecvBounded,
66// Close). Its lifetime is the caller's once returned: the one-stream guard
67// stays in force, so a second native stream fails the whole carriage instead of
68// becoming a logical attachment.
69func DialMuxContext(ctx context.Context, addr string, readiness Readiness, config Config, timeout time.Duration, opts ...Option) (wire.BoundedTransport, error) {
70	transport, err := Dial(ctx, addr, readiness, config, timeout, opts...)
71	if err != nil {
72		return nil, err
73	}
74	return asMuxCarriage(transport)
75}
76
77// AcceptMux waits for one authenticated peer on the bootstrap server and
78// returns its raw bounded carriage for daemonmux. It reuses the bootstrap
79// accept contract unchanged: the peer must present the single-use token and
80// nonce within the auth deadline, the token is consumed atomically, and every
81// failed or expired attempt closes its own carriage without handing anything
82// out. Because the credential is one-time, one Server is the daemon half of
83// exactly one physical daemonmux connection, so a daemon mints a fresh
84// ephemeral server per accepted physical connection.
85//
86// ctx (or the advertised credential lifetime, whichever ends first) bounds the
87// wait. A successful return transfers the carriage to the caller; daemonmux's
88// ServerSupervisor owns the physical lifetime from then on.
89func (s *Server) AcceptMux(ctx context.Context) (wire.BoundedTransport, error) {
90	if s == nil {
91		return nil, ErrMuxCarriage
92	}
93	transport, err := s.Accept(ctx)
94	if err != nil {
95		return nil, err
96	}
97	return asMuxCarriage(transport)
98}
99
100// asMuxCarriage asserts the one raw bounded capability daemonmux consumes. A
101// transport without it is closed rather than handed out unbounded.
102func asMuxCarriage(transport wire.Transport) (wire.BoundedTransport, error) {
103	bounded, ok := transport.(wire.BoundedTransport)
104	if !ok {
105		if transport != nil {
106			_ = transport.Close()
107		}
108		return nil, ErrMuxCarriage
109	}
110	return bounded, nil
111}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close