Help us shape the future of Go! Take the 2026 Go Developer Survey

principalwire

package
v1.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

Documentation

Overview

Package principalwire — ЕДИНСТВЕННОЕ объявление пространства имён личности на проводе (приёмка KAN-WIRE-1, сценарии KAN-W2-05 / KAN-W2-06, предмет `ПР-2`).

Что здесь объявлено и почему в одном месте

Личность арендатора едет от края к слушателю службы набором ключей под общей приставкой. У этого набора было ДВА независимых объявления — своё у края и своё у фундамента, — и общей константы между ними не существовало. Значит переименование одной стороны СОБИРАЛОСЬ ЧИСТО: приёмник, не найдя своих ключей, читал это как «личности нет» и шёл дальше. Отказом это не кончалось, потому что отсутствие личности в этом тракте отказом не является намеренно — фоновые пути ходят без неё.

Здесь объявление одно, поэтому расхождение двух написаний стало невыразимым: переименовать «одну сторону» больше нечего. Носители (`gateway/internal/principalmeta` у края, `pkg/grpcsrv` у фундамента) берут имена отсюда и своих литералов не держат — это проверяет гейт дерева `internal/repohygiene` `TestIdentityWireNamespaceIsDeclaredOnce`.

Три поверхностные формы ОДНОГО логического ключа

  • `Header*` — канонический HTTP-заголовок (к нему приводит `http.Header`);
  • `HeaderGRPCMeta*` — он же под мостовой приставкой: входящий HTTP-заголовок `Grpc-Metadata-Foo` приезжает слушателю метаданным `foo`;
  • `Meta*` — нижнерегистровый ключ метаданных gRPC, который читает слушатель.

Пары «заголовок ↔ метаданное» держатся вместе не соглашением, а пробой `TestKeyFormsAreOneKeyInThreeSurfaces`: метаданное обязано быть нижним регистром своего заголовка, а заголовок — каноническим.

Почему пакет лежит в фундаменте, а не у края

Направление зависимостей одно: край импортирует фундамент, обратного пути нет by construction (каталог края закрыт `internal`). Объявление, лежащее у края, фундаменту недоступно — а читает ключи именно он.

Чего здесь НЕТ намеренно

Здесь нет ПОЛИТИКИ: что край вычищает у клиента, что кладёт аннотатор, что пропускает мост — решения края, и живут они у края. Здесь — только имена и закрытый каталог свойств, о которых обеим сторонам надо договориться.

Index

Constants

View Source
const (
	Namespace    = "x-kacho-"
	BridgePrefix = "grpc-metadata-"
)
View Source
const (
	MetaPrincipalPrefix = "x-kacho-principal-"
	MetaTokenPrefix     = "x-kacho-token-" // #nosec G101 -- приставка имени ключа метаданных, не удостоверение
)

Подсемейства пространства. Приставка — то, чем ключуется вычистка чужого ввода: перечень известных имён устарел бы в день заведения одиннадцатого.

View Source
const (
	HeaderPrincipalType    = "X-Kacho-Principal-Type"
	HeaderPrincipalID      = "X-Kacho-Principal-Id"
	HeaderPrincipalDisplay = "X-Kacho-Principal-Display-Name"
	HeaderTokenACR         = "X-Kacho-Token-Acr"    // #nosec G101 -- имя HTTP-заголовка (утверждение `acr`), не удостоверение
	HeaderTokenJti         = "X-Kacho-Token-Jti"    // #nosec G101 -- имя HTTP-заголовка (утверждение `jti`), не удостоверение
	HeaderTokenScope       = "X-Kacho-Token-Scope"  // #nosec G101 -- имя HTTP-заголовка (утверждение `scope`), не удостоверение
	HeaderTokenExp         = "X-Kacho-Token-Exp"    // #nosec G101 -- имя HTTP-заголовка (утверждение `exp`), не удостоверение
	HeaderTokenAMR         = "X-Kacho-Token-Amr"    // #nosec G101 -- имя HTTP-заголовка (перечень способов подтверждения), не удостоверение
	HeaderTokenMfaAt       = "X-Kacho-Token-Mfa-At" // #nosec G101 -- имя HTTP-заголовка (момент подтверждения), не удостоверение

	// HeaderTokenBasicCredentialID — идентификатор СТРОКИ базового
	// удостоверения, а не сама строка: значение секрета этот заголовок не несёт.
	HeaderTokenBasicCredentialID = "X-Kacho-Token-Basic-Credential-Id" // #nosec G101 -- идентификатор строки, не удостоверение

	// HeaderTokenSessionID — НОМЕР ЗАПИСИ сессии человека, за которую край
	// проксирует запрос. Край узнаёт его из ответа службы о носителе и
	// возвращает службе с каждым запросом этой сессии: по нему снятие ключа
	// доступа оставляет текущую сессию живой. Номер ничего не аутентифицирует —
	// им нельзя назваться и нельзя продлить сессию; доверие к нему — то же, что
	// к пересланной личности (доверенный отправитель).
	HeaderTokenSessionID = "X-Kacho-Token-Session-Id" // #nosec G101 -- номер записи, не удостоверение
)

Канонические имена HTTP-заголовков.

View Source
const (
	HeaderGRPCMetaPrincipalType    = "Grpc-Metadata-" + HeaderPrincipalType
	HeaderGRPCMetaPrincipalID      = "Grpc-Metadata-" + HeaderPrincipalID
	HeaderGRPCMetaPrincipalDisplay = "Grpc-Metadata-" + HeaderPrincipalDisplay
	HeaderGRPCMetaTokenACR         = "Grpc-Metadata-" + HeaderTokenACR
	HeaderGRPCMetaTokenJti         = "Grpc-Metadata-" + HeaderTokenJti
	HeaderGRPCMetaTokenScope       = "Grpc-Metadata-" + HeaderTokenScope
	HeaderGRPCMetaTokenSessionID   = "Grpc-Metadata-" + HeaderTokenSessionID
)

Мостовые формы тех же заголовков. Собираются приставкой, а не переписываются руками: вторая запись имени разошлась бы с первой молча.

View Source
const (
	MetaPrincipalType    = "x-kacho-principal-type"
	MetaPrincipalID      = "x-kacho-principal-id"
	MetaPrincipalDisplay = "x-kacho-principal-display-name"

	// MetaPrincipalDisplayBin — ДВОИЧНАЯ форма отображаемого имени.
	//
	// Значение обычного ключа метаданных ограничено печатаемой латиницей, и имя,
	// записанное кириллицей, роняет ВЕСЬ вызов, не дойдя до обработчика. Продукт
	// русскоязычный, поэтому имя едет формой, допускающей произвольные байты.
	// Тип и идентификатор остаются обычными ключами: они латиница by construction.
	MetaPrincipalDisplayBin = "x-kacho-principal-display-name-bin"

	MetaTokenACR   = "x-kacho-token-acr"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenJti   = "x-kacho-token-jti"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenScope = "x-kacho-token-scope"  // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenAMR   = "x-kacho-token-amr"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenMfaAt = "x-kacho-token-mfa-at" // #nosec G101 -- имя ключа метаданных, не удостоверение

	// MetaTokenBasicCredentialID — нижнерегистровая форма
	// [HeaderTokenBasicCredentialID]. Объявлена не ради потребителя за краем —
	// его нет, — а ради ЗАПРЕТА: набор ключей края ведётся этими именами, и
	// ключ, которого в нём нет, мост пропустил бы молча.
	MetaTokenBasicCredentialID = "x-kacho-token-basic-credential-id" // #nosec G101 -- идентификатор строки, не удостоверение

	// MetaTokenSessionID — нижнерегистровая форма [HeaderTokenSessionID]:
	// ровно то, что читает служба доступа за вердиктом о доверенном
	// отправителе.
	MetaTokenSessionID = "x-kacho-token-session-id" // #nosec G101 -- номер записи, не удостоверение
)

Нижнерегистровые ключи метаданных gRPC: ровно то, что читает слушатель (`metadata.MD` приводит имя к нижнему регистру).

View Source
const ImportPath = "github.com/PRO-Robotech/corelib/principalwire"

Namespace — приставка КАЖДОГО ключа личности и контекста удостоверения на проводе. Всё под ней принадлежит краю по определению: значение выводится из проверенного удостоверения, и клиент не вправе его прислать.

BridgePrefix — приставка моста REST→gRPC: входящий заголовок `Grpc-Metadata-Foo` приезжает слушателю метаданным `foo`. Обе поверхностные формы одного ключа обязаны судиться одинаково любой политикой над пространством. ImportPath — путь импорта ЭТОГО пакета.

Стоит здесь затем, чтобы проверки дерева опознавали читателя ключа по ПОЛНОМУ пути импорта, а не по имени пакета: псевдоним (`pw "…/pkg/principalwire"`) пишется так же коротко, и разбор по имени обходился бы одной буквой в объявлении импорта.

Variables

This section is empty.

Functions

func Bare

func Bare(key string) string

Bare — голая нижнерегистровая форма имени: нижний регистр и снятая мостовая приставка. Пространству имён может и не принадлежать.

func IsEdgeOnly

func IsEdgeOnly(meta string) bool

IsEdgeOnly — остаётся ли ключ на краю (мост его не пропускает).

Имя приходит нормализованным: нижний регистр, без мостовой приставки — так его отдаёт NamespaceKey, и обе поверхностные формы сводятся к одному имени ещё до этого вопроса.

func IsIdentityShaped

func IsIdentityShaped(bare string) bool

IsIdentityShaped — имеет ли голое имя ФОРМУ ключа личности НЕЗАВИСИМО от того, чьё пространство имён над ним стоит: `x-<обозначение>-principal-<поле>` либо `x-<обозначение>-token-<поле>`.

Зачем свойство ФОРМЫ, когда есть своё пространство имён

Переименование пространства — ровно тот переход, ради которого написан этот пакет, и в окне перехода край и служба собраны из разных деревьев. Приёмник, умеющий спросить только про СВОЮ приставку, о чужой не узнаёт ничего: пересланная личность для него не приезжает вовсе, и он читает это как законную безымянность. Форма — единственный признак, по которому «личность приехала под именем, которого я не читаю» отличимо от «личности не было».

Признак нарочно узкий: он требует и подсемейства (`principal-`/`token-`), и непустого поля за ним. Соседи по проводу (`x-request-id`, `x-forwarded-for`, `x-envoy-…`) под него не подпадают, поэтому отказ не приходит на чужую служебную разметку.

func IsOurs

func IsOurs(bare string) bool

IsOurs — принадлежит ли голое имя пространству имён ЭТОЙ сборки.

func NamespaceKey

func NamespaceKey(key string) (string, bool)

NamespaceKey приводит входящее имя заголовка или ключа метаданных к голой нижнерегистровой форме и сообщает, принадлежит ли оно НАШЕМУ пространству. Мостовая приставка снимается первой, поэтому `Grpc-Metadata-X-Kacho-Admin` и `x-kacho-admin` дают одно и то же ("x-kacho-admin", true).

Types

type Key

type Key struct {
	// Name — имя записи для отказов и переписей. Не поверхностная форма.
	Name string
	// Ident — ИМЯ КОНСТАНТЫ этого пакета, несущей форму Meta.
	//
	// Записано данными затем, что после сведения объявления в одно читатели
	// пишут не литерал, а `principalwire.<Ident>` — и проверка, знающая только
	// литерал, перестала бы видеть читателя вовсе: ни красного, ни зелёного,
	// молчание. Соответствие имени значению держит проба
	// `TestCatalogueIdentsNameRealConstants`: она разбирает исходник этого же
	// пакета, поэтому расхождение невыразимо, а не запрещено соглашением.
	// Пусто, когда формы Meta нет.
	Ident string
	// Meta — нижнерегистровый ключ метаданных. Пусто, когда формы нет.
	Meta string
	// Header — канонический HTTP-заголовок. Пусто, когда формы нет
	// (`…-display-name-bin` живёт только метаданным).
	Header string

	// Fundament — фундамент (`pkg/grpcsrv`) читает этот ключ СВОИМ объявлением.
	//
	// Свойство объявлено ЗДЕСЬ, а держится гейтом дерева: ключ, помеченный
	// читаемым и не заведённый у фундамента, роняет проверку с ИМЕНАМИ ОБОИХ
	// файлов. Ровно тот рассинхрон, который прежде собирался чисто.
	Fundament bool

	// EdgeOnly — край производит ключ ДЛЯ СЕБЯ и за мост не пускает.
	//
	// Запись существует, чтобы односторонность была РЕШЕНИЕМ, а не умолчанием:
	// мост снимает приставку сам, поэтому голая форма пересекает его наравне с
	// мостовой, и «мостовой формы нет» односторонности не даёт.
	EdgeOnly bool
}

Key — одна запись КАТАЛОГА: логический ключ во всех формах, в которых он существует, плюс два свойства, о которых обе стороны провода обязаны договориться.

func Keys

func Keys() []Key

Keys возвращает копию каталога. Копия, а не срез-хранитель: каталог — объявление, и вызывающий, дописавший в него запись, объявлял бы контракт из своего пакета.

type WireNameKind

type WireNameKind int

WireNameKind — чем является строковое значение по отношению к пространству имён личности. Нужен проверкам дерева, чтобы отказ называл предмет, а не «строка похожа на нашу».

const (
	// WireNameNone — к пространству имён личности значение не относится.
	WireNameNone WireNameKind = iota
	// WireNameNamespace — само пространство имён.
	WireNameNamespace
	// WireNameFamily — приставка подсемейства (`x-<обозначение>-principal-`,
	// `x-<обозначение>-token-`). На ней ключуется вычистка чужого ввода, поэтому
	// второе её объявление так же опасно, как второе объявление ключа.
	WireNameFamily
	// WireNameKey — ключ формы личности.
	WireNameKey
)

Виды имён пространства.

func ClassifyWireName

func ClassifyWireName(v string) WireNameKind

ClassifyWireName относит строковое значение к пространству имён личности.

Почему признак СТРУКТУРНЫЙ, а не «начинается с нашей приставки»

Переименование пространства — тот самый переход, ради которого написан этот пакет. Признак, знающий только сегодняшнюю приставку, не увидел бы второго объявления, сделанного уже под новой, — то есть промолчал бы ровно в тот день, когда нужен.

Чего признак НЕ относит к пространству — названо, а не спрятано

Общая приставка ещё не делает имя ключом личности: `x-kacho-hook-token` (общий секрет крючка службы личности), `x-kacho-admin`, `x-kacho-project-id` — законные имена вне этого контракта, и запрет объявлять их значил бы запрет на соседние подсистемы. Различает подсемейство и непустое поле за ним.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL