use std::borrow::Cow;
use std::sync::Arc;
use lean_ctx_protocol::context_gateway::{
ClassificationV1, ContextDecisionV1, ContextDispositionV1, ReasonCodeV1, TransformationKindV1,
};
use serde_json::{Map, Value};
use super::super::capture::AdmissionCapture;
use super::super::{
AdmissionCounts, AdmissionPolicy, admit, admit_uninspectable, clean, fingerprint_digest,
mark_policy_withheld, mark_project_redaction, reason, withheld,
};
use crate::core::policy::runtime::ActivePolicy;
pub(super) struct Walk<'a> {
pub(super) policy: &'a AdmissionPolicy,
pub(super) fingerprint: Vec<u8>,
pub(super) capture: &'a AdmissionCapture,
pub(super) remote: bool,
pub(super) mandatory: bool,
pub(super) project: Option<Arc<ActivePolicy>>,
pub(super) wire: Wire,
pub(super) anthropic_safeguards: bool,
pub(super) refused: Option<String>,
pub(super) rewritten: bool,
pub(super) classification: Option<ClassificationV1>,
pub(super) nodes: usize,
pub(super) partial: bool,
pub(super) sealed: SealedWalk,
pub(super) provider: String,
pub(super) at: Location,
pub(super) withheld_classification: Option<ClassificationV1>,
}
#[derive(Default)]
pub(super) struct Location {
member: Option<String>,
message: Option<usize>,
}
impl Location {
fn describe(&self) -> String {
match (&self.member, self.message) {
(Some(member), Some(index)) if is_message_list(member) => format!("message {index}"),
(Some(member), _) => member.clone(),
(None, _) => "request".to_owned(),
}
}
}
fn is_message_list(member: &str) -> bool {
matches!(member, "messages" | "input" | "contents")
}
pub(super) fn withheld_notice(codes: &[&str], id: Option<&str>) -> String {
match id {
Some(id) => format!(
"[lean-ctx gateway: content withheld — {} · lean-ctx gateway why {id}]",
codes.join(", ")
),
None => format!(
"[lean-ctx gateway: content withheld — {}]",
codes.join(", ")
),
}
}
#[derive(Default)]
pub(super) struct SealedWalk {
parts: Vec<SealedPart>,
items: Vec<String>,
server_state: bool,
kind: Option<PartKind>,
}
enum Verdict {
Keep,
Replace(String),
}
impl Walk<'_> {
fn record(&self, decision: &ContextDecisionV1, counts: AdmissionCounts) {
if decision.disposition == ContextDispositionV1::Deny {
crate::core::hive_signals::admission_refused(
decision.reason_codes.iter().map(ReasonCodeV1::as_str),
);
}
self.capture.record(decision, counts);
}
fn observe(&mut self, classification: ClassificationV1) {
self.classification = Some(
self.classification
.map_or(classification, |seen| seen.join(classification)),
);
}
fn note_withheld(&self, decision: &ContextDecisionV1) -> Option<String> {
let id = withheld::block_id(&decision.object)?;
withheld::record(&withheld::WithheldBlock {
id: id.clone(),
reason_codes: decision
.reason_codes
.iter()
.map(|code| code.as_str().to_owned())
.collect(),
position: self.at.describe(),
provider: self.provider.clone(),
locality: if self.remote { "remote" } else { "local" }.to_owned(),
mode: format!("{:?}", self.policy.gateway.mode).to_ascii_lowercase(),
});
Some(id)
}
fn allowed_by_human(&self, decision: &ContextDecisionV1) -> bool {
!self.mandatory
&& withheld::block_id(&decision.object)
.is_some_and(|id| withheld::allowed(&id, &self.provider))
}
fn refuse(&mut self, reasons: &[ReasonCodeV1]) {
if self.refused.is_none() {
let codes: Vec<&str> = reasons.iter().map(ReasonCodeV1::as_str).collect();
self.refused = Some(format!(
"request withheld by the context gateway ({})",
codes.join(", ")
));
}
}
fn project_protected<'t>(&self, text: &'t str) -> Result<Cow<'t, str>, ()> {
let Some(active) = &self.project else {
return Ok(Cow::Borrowed(text));
};
let outcome = crate::core::policy::content::evaluate_text(text, active);
if outcome.blocked {
Err(())
} else {
Ok(Cow::Owned(outcome.text))
}
}
fn compose(&self, project_original: &str, gateway_output: &str) -> Option<String> {
let project_after_gateway = self.project_protected(gateway_output).ok()?.into_owned();
let gateway_after_project = admit(project_original, None, self.policy).text?;
(project_after_gateway == gateway_after_project).then_some(project_after_gateway)
}
fn admit_string(&mut self, text: &str, sealed: bool) -> Verdict {
let verdict = self.admit_string_inner(text, sealed);
if let Some(kind) = self.sealed.kind
&& !text.is_empty()
{
self.source_part(kind, text);
}
#[cfg(test)]
super::failpoint::after_first_string();
verdict
}
fn full_inspection(&self) -> bool {
self.mandatory || self.project.is_some()
}
fn source_part(&mut self, kind: PartKind, supplied: &str) {
if self.full_inspection() {
self.sealed.parts.push(SealedPart {
kind,
digest: crate::core::archive::delivered::digest(supplied),
});
}
}
fn admit_string_inner(&mut self, text: &str, sealed: bool) -> Verdict {
if text.is_empty() {
return Verdict::Keep;
}
let project_original = if self.project.is_some() {
if let Ok(admitted) = self.project_protected(text) {
Some(admitted)
} else {
let mut admission = admit(text, None, self.policy);
self.observe(admission.classification);
mark_policy_withheld(&mut admission, "policy.content_withheld");
self.note_withheld(&admission.decision);
self.record(&admission.decision, admission.counts);
self.refuse(&admission.decision.reason_codes);
return Verdict::Keep;
}
} else {
None
};
let project_rewrites = project_original
.as_ref()
.is_some_and(|admitted| admitted.as_ref() != text);
let key = self
.policy
.semantic
.is_none()
.then(|| clean::key(&self.fingerprint, "egress", text));
if !project_rewrites
&& let Some(key) = &key
&& let Some(decision) = clean::get(key)
{
self.observe(self.policy.gateway.mode.unclassified());
self.record(&decision, AdmissionCounts::default());
return Verdict::Keep;
}
let mut admission = admit(text, None, self.policy);
let mut destination_denied = false;
if self.remote
&& admission.text.is_some()
&& admission.classification >= ClassificationV1::Restricted
{
if self.allowed_by_human(&admission.decision) {
admission
.decision
.reason_codes
.push(reason(withheld::ALLOWED_ONCE));
self.observe(ClassificationV1::Confidential);
} else {
admission.text = None;
admission.decision.disposition = ContextDispositionV1::Deny;
admission
.decision
.reason_codes
.push(reason("destination.remote_restricted"));
destination_denied = true;
self.withheld_classification = Some(
self.withheld_classification
.map_or(admission.classification, |seen| {
seen.join(admission.classification)
}),
);
}
} else {
self.observe(admission.classification);
}
if let Some(project_original) = &project_original {
if destination_denied {
} else if let Some(gateway_output) = admission.text.as_deref() {
if let Some(composed) = self.compose(project_original.as_ref(), gateway_output) {
if project_rewrites || composed != gateway_output {
mark_project_redaction(&mut admission);
}
admission.text = Some(composed);
} else {
mark_policy_withheld(&mut admission, "policy.content_withheld");
self.refuse(&admission.decision.reason_codes);
}
} else if project_rewrites {
mark_policy_withheld(&mut admission, "policy.content_withheld");
self.refuse(&admission.decision.reason_codes);
}
}
if sealed && project_rewrites {
mark_policy_withheld(&mut admission, "policy.content_withheld");
self.refuse(&admission.decision.reason_codes);
}
if !project_rewrites
&& sealed
&& admission
.text
.as_deref()
.is_some_and(|delivered| delivered != text)
{
admission
.decision
.reason_codes
.push(reason("egress.sealed_content"));
admission
.decision
.required_transformations
.retain(|kind| *kind != TransformationKindV1::Redaction);
if self.mandatory {
admission.decision.disposition = ContextDispositionV1::Deny;
admission.text = None;
} else {
admission.decision.disposition = ContextDispositionV1::Allow;
admission.text = Some(text.to_owned());
}
}
self.record(&admission.decision, admission.counts);
if let Some(key) = key
&& admission.disposition() == ContextDispositionV1::Allow
&& admission.decision.reason_codes.is_empty()
&& admission.text.as_deref() == Some(text)
{
clean::insert(key, admission.decision.clone());
}
match admission.text {
Some(delivered) if delivered == text => Verdict::Keep,
Some(delivered) => {
self.rewritten = true;
Verdict::Replace(delivered)
}
None if sealed => {
self.note_withheld(&admission.decision);
self.refuse(&admission.decision.reason_codes);
Verdict::Keep
}
None => {
let id = self.note_withheld(&admission.decision);
let codes: Vec<&str> = admission
.decision
.reason_codes
.iter()
.map(ReasonCodeV1::as_str)
.collect();
self.rewritten = true;
Verdict::Replace(withheld_notice(&codes, id.as_deref()))
}
}
}
fn media(&mut self, bytes: usize) {
let admission = admit_uninspectable(None, bytes as u64, self.policy);
self.record(&admission.decision, admission.counts);
if admission.text.is_none() {
self.refuse(&admission.decision.reason_codes);
}
}
fn verify_immutable(&mut self, text: &str, protocol_flag: bool) {
if text.is_empty() || self.refused.is_some() {
return;
}
if self.project.is_some()
&& !self
.project_protected(text)
.is_ok_and(|admitted| admitted.as_ref() == text)
{
let codes = [
reason("policy.content_withheld"),
reason("egress.immutable_position"),
];
self.record(
&ContextDecisionV1 {
object: fingerprint_digest(b"egress-immutable-position"),
disposition: ContextDispositionV1::Deny,
reason_codes: codes.to_vec(),
signals: Vec::new(),
required_transformations: Vec::new(),
},
AdmissionCounts::default(),
);
self.refuse(&codes);
return;
}
let key = (!protocol_flag && self.policy.semantic.is_none())
.then(|| clean::key(&self.fingerprint, "egress", text));
if let Some(key) = &key
&& clean::get(key).is_some()
{
return;
}
let protocol_policy = protocol_flag.then(|| {
let mut policy = self.policy.clone();
policy.gateway.classification = crate::core::input_filters::FilterAction::Off;
policy
});
let mut admission = admit(text, None, protocol_policy.as_ref().unwrap_or(self.policy));
let mut restricted =
self.remote && admission.classification >= ClassificationV1::Restricted;
if restricted && self.allowed_by_human(&admission.decision) {
restricted = false;
admission
.decision
.reason_codes
.push(reason(withheld::ALLOWED_ONCE));
admission.classification = ClassificationV1::Confidential;
}
if admission.text.as_deref() == Some(text) && !restricted {
if admission.decision.reason_codes.is_empty() {
if let Some(key) = key
&& admission.disposition() == ContextDispositionV1::Allow
{
clean::insert(key, admission.decision.clone());
}
} else {
self.observe(admission.classification);
self.record(&admission.decision, admission.counts);
}
return;
}
self.observe(admission.classification);
if restricted {
admission
.decision
.reason_codes
.push(reason("destination.remote_restricted"));
}
admission
.decision
.reason_codes
.push(reason("egress.immutable_position"));
admission.decision.disposition = ContextDispositionV1::Deny;
admission
.decision
.required_transformations
.retain(|kind| *kind != TransformationKindV1::Redaction);
self.note_withheld(&admission.decision);
self.record(&admission.decision, admission.counts);
self.refuse(&admission.decision.reason_codes);
}
fn opaque(&mut self, member: &Value) {
let sealed = member
.as_str()
.map_or_else(|| Cow::Owned(member.to_string()), Cow::Borrowed);
if self.full_inspection() {
self.sealed.items.push(sealed.into_owned());
} else {
self.record_sealed(&sealed, None);
}
}
pub(super) fn settle_sealed(&mut self) {
if !self.full_inspection() {
return;
}
let sealed = std::mem::take(&mut self.sealed);
let verdicts = resolve_sealed(&SealedRequest {
items: &sealed.items,
parts: &sealed.parts,
server_state: sealed.server_state,
});
for (index, item) in sealed.items.iter().enumerate() {
let verdict = verdicts
.get(index)
.copied()
.unwrap_or(SealedVerdict::UnknownOrigin);
self.record_sealed(item, Some(verdict));
}
}
fn record_sealed(&mut self, sealed: &str, verdict: Option<SealedVerdict>) {
let size_only =
|| fingerprint_digest(format!("opaque-provider-material:{}", sealed.len()).as_bytes());
let (disposition, object, verdict_code) = match verdict {
None => (ContextDispositionV1::Allow, size_only(), None),
Some(SealedVerdict::ReturnedToOrigin) => (
ContextDispositionV1::Allow,
fingerprint_digest(sealed.as_bytes()),
Some("egress.sealed_returned_to_origin"),
),
Some(SealedVerdict::UnknownOrigin) => (
ContextDispositionV1::Deny,
size_only(),
Some("egress.sealed_origin_unknown"),
),
Some(SealedVerdict::SourceAuthorityUnknown) => (
ContextDispositionV1::Deny,
size_only(),
Some("egress.sealed_source_authority_unknown"),
),
Some(SealedVerdict::SourceNotPresented) => (
ContextDispositionV1::Deny,
size_only(),
Some("egress.sealed_source_not_presented"),
),
Some(SealedVerdict::AuthorityLapsed) => (
ContextDispositionV1::Deny,
size_only(),
Some("egress.sealed_authority_lapsed"),
),
};
let mut codes = vec![reason(OPAQUE_PROVIDER_MATERIAL)];
if let Some(code) = verdict_code {
codes.push(reason(code));
codes.push(reason("coverage.uninspected"));
}
self.record(
&ContextDecisionV1 {
object,
disposition,
reason_codes: codes.clone(),
signals: Vec::new(),
required_transformations: Vec::new(),
},
AdmissionCounts::default(),
);
if disposition == ContextDispositionV1::Deny {
self.refuse(&codes);
}
}
fn bounded(&mut self, depth: usize) -> bool {
self.nodes += 1;
if self.nodes <= MAX_WALK_NODES && depth <= MAX_WALK_DEPTH {
return true;
}
if !self.partial {
self.partial = true;
let codes = [reason("coverage.partial")];
let refuse = self.mandatory || self.project.is_some();
self.record(
&ContextDecisionV1 {
object: fingerprint_digest(b"egress-walk-bound"),
disposition: if refuse {
ContextDispositionV1::Deny
} else {
ContextDispositionV1::Allow
},
reason_codes: codes.to_vec(),
signals: Vec::new(),
required_transformations: Vec::new(),
},
AdmissionCounts::default(),
);
if refuse {
self.refuse(&codes);
}
}
false
}
pub(super) fn walk(
&mut self,
value: &mut Value,
position: Position,
sealed: bool,
depth: usize,
) {
if !self.bounded(depth) {
return;
}
match value {
Value::String(text) => {
if let Verdict::Replace(admitted) = self.admit_string(text, sealed) {
*text = admitted;
}
}
Value::Array(items) => {
for (index, item) in items.iter_mut().enumerate() {
if depth == 1 {
self.at.message = Some(index);
}
let next = if position == Position::Safeguard && !item.is_object() {
Position::Content
} else {
position
};
self.walk(item, next, sealed, depth + 1);
}
}
Value::Object(map) => {
let opaque_members = provider_sealed(self.wire, position, map);
let sealed_block = position.protocol() && (sealed || !opaque_members.is_empty());
let outer = self.sealed.kind;
let kind = part_kind(position, map, outer);
if position == Position::Item
&& map.get("type").and_then(Value::as_str) == Some("item_reference")
{
self.sealed.server_state = true;
}
for (key, child) in map.iter_mut() {
let key = key.as_str();
if depth == 0 {
self.at = Location {
member: Some(key.to_owned()),
message: None,
};
}
self.sealed.kind = kind;
if refers_to_provider_state(position, key, child) {
self.sealed.server_state = true;
}
let protocol_flag = self.anthropic_safeguards
&& position == Position::ClassifierContext
&& key == "restricted"
&& child.is_boolean();
self.verify_immutable(key, protocol_flag);
let next = match (position, key) {
(Position::Root, "safeguards")
if self.anthropic_safeguards && child.is_array() =>
{
Child::Walk(Position::Safeguard)
}
(Position::Safeguard, "classifier_context") if child.is_object() => {
Child::Walk(Position::ClassifierContext)
}
_ => position.child(key, opaque_members),
};
match next {
Child::Identifier => {}
Child::Name => {
if let Value::String(name) = child {
self.verify_immutable(name, false);
} else {
self.walk(child, Position::Content, false, depth + 1);
}
}
Child::Opaque => self.opaque(child),
Child::Walk(next) => {
if position == Position::Block
&& let Some(bytes) = media_bytes(key, child)
{
self.media(bytes);
if let Value::String(payload) = &*child {
self.source_part(kind.unwrap_or(PartKind::Source), payload);
}
continue;
}
let sealed = sealed_block && (child.is_string() || next.protocol());
self.walk(child, next, sealed, depth + 1);
}
}
}
self.sealed.kind = outer;
}
_ => {}
}
}
}
const MAX_WALK_NODES: usize = 200_000;
const MAX_WALK_DEPTH: usize = 64;
const OPAQUE_PROVIDER_MATERIAL: &str = "egress.opaque_provider_material";
fn part_kind(
position: Position,
map: &Map<String, Value>,
outer: Option<PartKind>,
) -> Option<PartKind> {
let member = |key: &str| map.get(key).and_then(Value::as_str);
match position {
Position::Item => match (member("role"), member("type")) {
(Some("assistant" | "model"), _) => Some(PartKind::Assistant),
(Some("tool" | "function"), _) => Some(PartKind::Source),
(Some(_), _) | (None, None | Some("message" | "mcp_approval_response")) => None,
(None, Some(kind)) if kind.ends_with("_output") || kind == "item_reference" => {
Some(PartKind::Source)
}
(None, Some(_)) => Some(PartKind::Assistant),
},
Position::Block if outer.is_none() => {
let source = match member("type") {
Some(kind) => {
kind.ends_with("tool_result")
|| matches!(
kind,
"document"
| "image"
| "image_url"
| "input_image"
| "input_file"
| "file"
| "input_audio"
| "audio"
| "video"
| "search_result"
| "container_upload"
)
}
None => [
"functionResponse",
"function_response",
"inlineData",
"inline_data",
"fileData",
"file_data",
]
.iter()
.any(|shape| map.contains_key(*shape)),
};
source.then_some(PartKind::Source)
}
_ => outer,
}
}
fn refers_to_provider_state(position: Position, key: &str, value: &Value) -> bool {
if value.is_null() {
return false;
}
match (position, key) {
(
Position::Root,
"previous_response_id" | "conversation" | "prompt" | "cachedContent" | "cached_content",
)
| (Position::Block, "file_id" | "fileData" | "file_data" | "fileUri" | "file_uri") => true,
(Position::Block, "url" | "image_url") => value
.as_str()
.is_some_and(|url| url.starts_with("http://") || url.starts_with("https://")),
_ => false,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SealedVerdict {
ReturnedToOrigin,
UnknownOrigin,
SourceAuthorityUnknown,
SourceNotPresented,
AuthorityLapsed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PartKind {
Source,
Assistant,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SealedPart {
pub kind: PartKind,
pub digest: String,
}
#[derive(Debug, Clone, Copy)]
pub struct SealedRequest<'a> {
pub items: &'a [String],
pub parts: &'a [SealedPart],
pub server_state: bool,
}
type SealedResolver = Box<dyn Fn(&SealedRequest<'_>) -> Vec<SealedVerdict>>;
thread_local! {
static SEALED_RESOLVER: std::cell::RefCell<Option<SealedResolver>> =
const { std::cell::RefCell::new(None) };
}
pub(super) fn with_resolver<T>(resolver: SealedResolver, operation: impl FnOnce() -> T) -> T {
struct Restore(Option<SealedResolver>);
impl Drop for Restore {
fn drop(&mut self) {
let previous = self.0.take();
SEALED_RESOLVER.with(|slot| *slot.borrow_mut() = previous);
}
}
let _restore = Restore(SEALED_RESOLVER.with(|slot| slot.borrow_mut().replace(resolver)));
operation()
}
fn resolve_sealed(request: &SealedRequest<'_>) -> Vec<SealedVerdict> {
SEALED_RESOLVER.with(|slot| {
slot.borrow()
.as_ref()
.map_or_else(Vec::new, |resolver| resolver(request))
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Wire {
Anthropic,
OpenAi,
Gemini,
Unknown,
}
impl Wire {
pub(super) fn of(provider: &str) -> Self {
match provider.to_ascii_lowercase().as_str() {
"anthropic" | "bedrock" | "copilot" => Self::Anthropic,
"openai" | "chatgpt" | "grok" | "commandcode" => Self::OpenAi,
"gemini" | "google" => Self::Gemini,
_ => Self::Unknown,
}
}
fn speaks(self, family: Self) -> bool {
self == family || self == Self::Unknown
}
}
fn has_string(map: &Map<String, Value>, key: &str) -> bool {
map.get(key).is_some_and(Value::is_string)
}
fn provider_sealed(
wire: Wire,
position: Position,
map: &Map<String, Value>,
) -> &'static [&'static str] {
let kind = map.get("type").and_then(Value::as_str);
match (position, kind) {
(Position::Block, Some("thinking"))
if wire.speaks(Wire::Anthropic) && has_string(map, "signature") =>
{
&["signature"]
}
(Position::Block, Some("redacted_thinking"))
if wire.speaks(Wire::Anthropic) && has_string(map, "data") =>
{
&["data"]
}
(Position::Block, None)
if wire.speaks(Wire::Gemini)
&& (has_string(map, "thoughtSignature")
|| has_string(map, "thought_signature"))
&& ["functionCall", "function_call", "text"]
.iter()
.any(|member| map.contains_key(*member)) =>
{
&["thoughtSignature", "thought_signature"]
}
(Position::Item, Some("reasoning" | "compaction"))
if wire.speaks(Wire::OpenAi) && has_string(map, "encrypted_content") =>
{
&["encrypted_content"]
}
(Position::Item, Some("program"))
if wire.speaks(Wire::OpenAi) && has_string(map, "fingerprint") =>
{
&["fingerprint"]
}
_ => &[],
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Position {
Root,
Item,
Block,
Call,
Safeguard,
ClassifierContext,
Content,
}
enum Child {
Identifier,
Name,
Opaque,
Walk(Position),
}
impl Position {
fn protocol(self) -> bool {
matches!(self, Self::Root | Self::Item | Self::Block | Self::Call)
}
fn child(self, key: &str, opaque_members: &[&str]) -> Child {
if opaque_members.contains(&key) {
return Child::Opaque;
}
match (self, key) {
(Self::Root, "model" | "previous_response_id")
| (Self::Item, "role" | "type" | "id" | "call_id" | "tool_call_id" | "status")
| (
Self::Block,
"type" | "id" | "tool_use_id" | "call_id" | "file_id" | "media_type" | "mime_type"
| "mimeType",
)
| (Self::Call, "id" | "type") => Child::Identifier,
(Self::Root, "messages" | "input" | "contents") => Child::Walk(Self::Item),
(
Self::Root,
"system" | "instructions" | "systemInstruction" | "system_instruction",
)
| (Self::Item, "content" | "parts" | "summary" | "output")
| (
Self::Block,
"content" | "parts" | "source" | "inlineData" | "inline_data" | "image_url"
| "fileData" | "file_data" | "input_audio",
) => Child::Walk(Self::Block),
(Self::Item, "tool_calls")
| (
Self::Block,
"functionCall" | "functionResponse" | "function_call" | "function_response",
)
| (Self::Call, "function") => Child::Walk(Self::Call),
(Self::Item | Self::Block | Self::Call, "name") => Child::Name,
_ => Child::Walk(Self::Content),
}
}
}
fn media_bytes(key: &str, value: &Value) -> Option<usize> {
match (key, value) {
("data", Value::String(data)) if data.len() > 256 && looks_base64(data) => Some(data.len()),
("url" | "image_url" | "file_data", Value::String(url)) if url.starts_with("data:") => {
Some(url.len())
}
_ => None,
}
}
fn looks_base64(data: &str) -> bool {
data.bytes()
.take(512)
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'/' | b'=' | b'-' | b'_'))
}