// Package auth is maven's authority layer — the 4-layer cascade and the
// "surface caps authority" invariant.
//
// Spec contract (from docs/design.md § Auth):
//
//	a cascade, not a pick-one — each layer answers a different question:
//
//	  | layer | question                          | mechanism                    | surface                |
//	  | 0     | on the network at all?            | wireguard                    | floor                  |
//	  | 1     | enrolled box?                     | mTLS client cert             | pc client, authed page |
//	  | 2     | you, this session?                | passkey / webauthn           | pc client, authed page |
//	  | 3     | you, right now, for this act?     | passkey user-verification     | step-up acts           |
//
//	wg is necessary-not-sufficient: an unlocked laptop inside the tunnel is
//	"authed" at layer 0 only — that gap is why the upper layers exist.
//
// the invariant: auth tier is a property of the SURFACE; the surface caps
// maximum authority. you can't step up past what the channel structurally
// carries. voice STOPS at L0 (a room mic is reachable by anyone present →
// speaker verification is attribution, not auth). telegram inbound = weak
// tier (read + soft acts, never destructive, never registration). only
// pc_client / authed_page carry passkey user-verification (L3) at all.
//
// "compromised X can't forge Y" is the through-line — applied at smaller
// and smaller scope (network → box → process). the auth layer applies it at
// the process radius: a module calling CoreAPI is bound to a Surface; the
// Surface caps what methods/authority that call can carry.
package auth

// Layer — one rung of the auth cascade.
type Layer int8

const (
	// Layer0 — wireguard: on the network at all. Floor for everything.
	Layer0 Layer = 0
	// Layer1 — mTLS client cert: enrolled box. Optional per spec ("if dropping
	// one, drop mTLS, never the passkey"); not wired by the floor enrollment.
	Layer1 Layer = 1
	// Layer2 — passkey/webauthn session: you, this session.
	Layer2 Layer = 2
	// Layer3 — passkey user-verification gesture for a SINGLE act: you, right
	// now, for this. registration-enable, destructive acts, core cold-start
	// unlock. the highest-authority op.
	Layer3 Layer = 3
)

// Surface — where a call ENTERS maven from. A property of the channel that
// structurally caps the maximum authority that channel can carry. The wire
// doesn't carry a layer — it carries a Caller; the Enrollment maps the caller
// to a Surface; MaxLayer caps it. So voice can never reach EnableTool — not
// because auth "failed" but because the channel can't carry the proof.
type Surface string

const (
	// SurfaceVoice — a room mic / wake-word path. Speaker verification is
	// attribution, not auth: anyone present (gf, the TV) can speak. STOPS at
	// L0. never destructive, never registration.
	SurfaceVoice Surface = "voice"
	// SurfaceTelegram — telegram inbound. weak tier: telegram's own auth,
	// outside our control. read + soft acts, never destructive, never
	// registration. carries up to L2 (a chat-id allowlist is the best we get).
	SurfaceTelegram Surface = "telegram"
	// SurfacePCClient — the desktop gui, mTLS'd and passkey'd. carries L3
	// (passkey user-verification is available on-device).
	SurfacePCClient Surface = "pc_client"
	// SurfaceAuthedPage — the web authed page over wg. carries L3 (passkey
	// user-verification via the browser / platform authenticator).
	SurfaceAuthedPage Surface = "authed_page"
	// SurfaceCoreProcess — a module running in core's own address space (the
	// daemon-embedded router/delivery today). There is no boundary to cross;
	// this is the 0600-floor equivalent: trusted same-process. Layer3-capped
	// since the user already unlocked the daemon (cold-start IS L3 per spec).
	SurfaceCoreProcess Surface = "core_process"
	// SurfaceUnknown — enrollment didn't recognize the caller. fail closed.
	SurfaceUnknown Surface = "unknown"
)

// MaxLayer — the surface-caps-authority table. PURE. The invariant: you
// cannot step up past what your channel structurally carries. voice → L0;
// telegram → L2 (chat allowlist); pc_client / authed_page / core_process →
// L3. An unrecognized surface caps at -1 (fail closed) — a caller with no
// enrolled identity is not "unauthed at L0", it's refused outright.
func MaxLayer(s Surface) Layer {
	switch s {
	case SurfaceVoice:
		return Layer0
	case SurfaceTelegram:
		return Layer2
	case SurfacePCClient, SurfaceAuthedPage, SurfaceCoreProcess:
		return Layer3
	default:
		return -1
	}
}
