~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
main
570 lines · 24.1 KB Raw History
// socialweb.computer market policies as step protocol modules.
// One contract for every policy: entry `evaluate`, input = the kcp policy action's inputs
// { args, perspective, mode: "scope"|"full", selfDid, counterpartyDid, subjectDid, rootRequesterDid,
//   rfp?, bid?, accept? } plus an optional host-seeded `snapshot` { operatorOf, vouchedBy }; result =
// kcp's Verdict { allow, violations: [{ msg, policyId }] }.
//
// Decisions follow socialweb-computer-kcp (atlibs-bidder-fixes-rc159): the trust snapshot is
// trust.BuildSnapshotWith (internal/policyengine/trust/trust.go:316) with the default association
// kinds (claim + association, trust/association.go:23), the decisions are deno-typescript-shared
// trust.ts (onlyMeAllows, tangledVouchAllows, mutualsAllows), scope.ts scopeDecide and the work
// policies bid-payload.ts / under-4-cpus.ts. Without a `snapshot` the module gathers it itself with
// public com.atproto.repo.getRecord reads: every handler call re-runs the decision over the reads
// it has, asks for the ones it is missing, and answers as soon as nothing is missing.
use abi::{Call, Event, Handle, Run};
use serde_json::{Map, Value, json};
use sha2::{Digest, Sha256};

pub const GET_RECORD: &str = "com.atproto.repo.getRecord";
const VOUCH: &str = "sh.tangled.graph.vouch";
const BADGE: &str = "com.publicdomainrelay.temp.badgeBlueKeys";
const ASSOCIATION: &str = "com.publicdomainrelay.temp.market.association";
const ACCEPT: &str = "com.publicdomainrelay.temp.market.accept";
const RECEIPT: &str = "com.publicdomainrelay.temp.market.receipt";
const VM_DELETE: &str = "com.publicdomainrelay.temp.compute.events.vm.delete";
const VM_TERMINATED: &str = "com.publicdomainrelay.temp.compute.events.vm.terminated";
const BIDS_FREE: &str = "com.publicdomainrelay.temp.market.bids.free";
const BIDDER_ASSOCIATE: &str = "bidder_associate";
const REQUESTER_ASSOCIATE: &str = "requester_associate";

#[derive(Clone, Copy, PartialEq)]
pub enum Kind {
    OnlyMe,
    TangledVouch,
    Mutuals,
    Open,
    BidPayload,
    UnderCpus,
}

pub struct Policy {
    pub name: &'static str,
    pub kind: Kind,
}

impl Policy {
    // action-common.ts policyPerspective: a work policy's first perspective, else the name's side.
    fn perspective(&self) -> &'static str {
        match self.kind {
            Kind::BidPayload => "requester",
            Kind::UnderCpus => "bidder",
            _ if self.name.starts_with("bidder-") => "bidder",
            _ => "requester",
        }
    }

    fn trust(&self) -> bool {
        matches!(self.kind, Kind::OnlyMe | Kind::TangledVouch | Kind::Mutuals)
    }
}

/// The handler every policy module exports as `evaluate`.
pub fn evaluate(policy: &Policy, run: Run<Value>) -> Result<Handle, String> {
    // got: at-uri -> getRecord output (null: no such record, {$error}: the read failed);
    // issued: call id -> at-uri (call ids are short).
    let mut got = run.state.get("got").and_then(Value::as_object).cloned().unwrap_or_default();
    let mut issued = run.state.get("issued").and_then(Value::as_object).cloned().unwrap_or_default();
    match run.event {
        Event::Start => {}
        Event::Result { id, result, .. } => {
            let Some(uri) = issued.get(&id).and_then(Value::as_str) else { return Ok(Handle::new()) };
            let value = match result.output() {
                Ok(output) => output.clone(),
                Err(error) if error.contains("RecordNotFound") => Value::Null,
                Err(error) => json!({ "$error": error }),
            };
            got.insert(uri.to_string(), value);
        }
        _ => return Ok(Handle::new()),
    }
    let mut reads = Reads { got: &got, need: Vec::new() };
    if let Some(verdict) = decide(policy, &run.args, &mut reads)? {
        return Ok(Handle::new().result(verdict).done());
    }
    let need: Vec<String> = reads.need.into_iter().filter(|uri| !issued.values().any(|u| u == uri)).collect();
    if need.is_empty() && issued.values().all(|uri| uri.as_str().is_some_and(|u| got.contains_key(u))) {
        return Err("policy stalled: no reads pending and no verdict".into());
    }
    let mut out = Handle::new();
    for uri in need {
        let id = format!("r{}", issued.len());
        let (repo, collection, rkey) = split(&uri);
        out = out.call(Call::new(&id, GET_RECORD).params(json!({ "repo": repo, "collection": collection, "rkey": rkey })).service(repo));
        issued.insert(id, json!(uri));
    }
    Ok(out.state(json!({ "got": got, "issued": issued })))
}

fn verdict(policy: &Policy, deny: Option<String>) -> Value {
    match deny {
        None => json!({ "allow": true, "violations": [] }),
        Some(msg) => json!({ "allow": false, "violations": [{ "msg": msg, "policyId": policy.name }] }),
    }
}

fn text<'a>(input: &'a Value, key: &str) -> &'a str {
    input[key].as_str().map(str::trim).unwrap_or("")
}

// Ok(None): a read is missing (asked for in reads.need).
fn decide(policy: &Policy, input: &Value, reads: &mut Reads) -> Result<Option<Value>, String> {
    let self_did = text(input, "selfDid");
    if self_did.is_empty() {
        return Err("selfDid is required (the evaluator's own DID)".into());
    }
    let perspective = match text(input, "perspective") {
        "" => policy.perspective(),
        p => p,
    };
    let args = &input["args"];
    if input["mode"] == "scope" {
        // action-common.ts evaluateScope: the counterparty is the subject; work policies abstain in
        // scopeDecide and their full evaluation then sees no records, so they allow.
        let cp = text(input, "counterpartyDid");
        if cp.is_empty() {
            return Err("scope mode requires counterpartyDid".into());
        }
        if !policy.trust() {
            return Ok(Some(verdict(policy, None)));
        }
        let Some(snapshot) = snapshot_for(input, reads, perspective, self_did, cp) else { return Ok(None) };
        let deny = (!allows(policy.kind, &snapshot, self_did, cp)).then(|| format!("scope denied by {}", policy.name));
        return Ok(Some(verdict(policy, deny)));
    }
    // ctx.ts createPolicyCtx: subject from the records when not given, counterparty defaults to it.
    let subject = match text(input, "subjectDid") {
        "" if perspective == "requester" => author(&input["bid"]),
        "" if input["rfp"].is_object() => author(&input["rfp"]),
        "" => author(&input["accept"]),
        s => s,
    };
    let cp = match text(input, "counterpartyDid") {
        "" => subject,
        c => c,
    };
    Ok(match policy.kind {
        Kind::Open => Some(verdict(policy, None)),
        Kind::OnlyMe | Kind::TangledVouch | Kind::Mutuals => {
            // trust.ts evaluateTrust
            if cp.is_empty() {
                return Ok(Some(verdict(policy, Some("no counterparty to evaluate".into()))));
            }
            if !subject.is_empty() && subject != cp {
                return Ok(Some(verdict(policy, Some("the subject and the counterparty differ".into()))));
            }
            let Some(snapshot) = snapshot_for(input, reads, perspective, self_did, cp) else { return Ok(None) };
            let msg = match policy.kind {
                Kind::OnlyMe => "operator mismatch",
                Kind::TangledVouch => "not vouched",
                _ => "not mutual follows",
            };
            Some(verdict(policy, (!allows(policy.kind, &snapshot, self_did, cp)).then(|| msg.into())))
        }
        Kind::BidPayload => {
            // bid-payload.ts: the bid's payload collection must be allowed (free bids by default).
            let Some(bid) = hydrate(reads, &input["bid"]) else { return Ok(None) };
            let Some(nsid) = payload_ref(&bid).map(|uri| split(uri).1.to_string()) else { return Ok(Some(verdict(policy, None))) };
            let free = [json!(BIDS_FREE)];
            let allowed = args["allowedPayloadNsids"].as_array().map(Vec::as_slice).unwrap_or(&free);
            let deny = (!allowed.contains(&json!(nsid))).then(|| format!("bid payload type {nsid} not allowed"));
            Some(verdict(policy, deny))
        }
        Kind::UnderCpus => {
            // under-4-cpus.ts: the RFP's payload cpus must be at most args.maxCpus (a number, else 4).
            let max = args["maxCpus"].as_f64().unwrap_or(4.0);
            let Some(rfp) = hydrate(reads, &input["rfp"]) else { return Ok(None) };
            let Some(uri) = payload_ref(&rfp) else { return Ok(Some(verdict(policy, None))) };
            let payload = match &input["rfp"]["payload"] {
                p if truthy(p) => p.clone(),
                _ => match reads.get(uri) {
                    None => return Ok(None),
                    Some(Rec::Found(output)) => output.get("value").cloned().unwrap_or_else(|| json!({})),
                    Some(Rec::Absent) => return Ok(Some(verdict(policy, Some(format!("failed to resolve demand payload: Error: getRecord {uri} failed: RecordNotFound"))))),
                    Some(Rec::Failed(e)) => return Ok(Some(verdict(policy, Some(format!("failed to resolve demand payload: Error: getRecord {uri} failed: {e}"))))),
                },
            };
            let deny = payload["cpus"].as_f64().filter(|cpus| *cpus > max).map(|cpus| format!("VM needs {} cpus; this bidder's cap is {}", js_number(cpus), js_number(max)));
            Some(verdict(policy, deny))
        }
    })
}

fn truthy(value: &Value) -> bool {
    match value {
        Value::Null => false,
        Value::Bool(b) => *b,
        Value::Number(n) => n.as_f64().is_some_and(|n| n != 0.0),
        Value::String(s) => !s.is_empty(),
        _ => true,
    }
}

fn js_number(n: f64) -> String {
    if n.fract() == 0.0 && n.abs() < 1e21 { format!("{}", n as i64) } else { format!("{n}") }
}

fn author(record: &Value) -> &str {
    record["uri"].as_str().map(|uri| split(uri).0).unwrap_or("")
}

// ctx.ts hydrate: a record given as a ref is read; a failed read keeps the ref without a value.
fn hydrate(reads: &mut Reads, record: &Value) -> Option<Value> {
    if !record.is_object() || truthy(&record["value"]) {
        return Some(record["value"].clone());
    }
    let Some(uri) = record["uri"].as_str() else { return Some(Value::Null) };
    Some(match reads.get(uri)? {
        Rec::Found(output) => output.get("value").cloned().unwrap_or_else(|| json!({})),
        _ => Value::Null,
    })
}

// ctx.ts deriveOffer / deriveDemand: the record's payload strongRef uri, when it has uri and cid.
fn payload_ref(value: &Value) -> Option<&str> {
    let payload = &value["payload"];
    (truthy(&payload["uri"]) && truthy(&payload["cid"])).then(|| payload["uri"].as_str()).flatten()
}

// ---- trust decisions ---------------------------------------------------------------------------

pub struct Snapshot {
    operator_of: Map<String, Value>,
    vouched_by: Map<String, Value>,
}

impl Snapshot {
    fn operator(&self, did: &str) -> Option<&str> {
        self.operator_of.get(did).and_then(Value::as_str).filter(|op| !op.is_empty())
    }

    // A self-vouch never counts (BuildSnapshotWith skips subject == voucher, trust.go:391).
    fn vouched(&self, voucher: &str, subject: &str) -> bool {
        voucher != subject && self.vouched_by.get(voucher).and_then(Value::as_array).is_some_and(|l| l.iter().any(|d| d == subject))
    }
}

// trust.ts onlyMeAllows / tangledVouchAllows / mutualsAllows over scope.ts buildTrustSnapshot.
fn allows(kind: Kind, s: &Snapshot, self_did: &str, did: &str) -> bool {
    let trusted = [self_did, s.operator(self_did).unwrap_or(self_did)];
    let op = s.operator(did);
    let only_me = did == self_did || op.is_some_and(|op| trusted.contains(&op));
    let trust_vouched = || trusted.iter().any(|t| s.vouched(t, did) || op.is_some_and(|op| s.vouched(t, op)));
    let vouches_trusted = || {
        let vouchers: &[&str] = match op {
            Some(op) if op != did => &[did, op],
            _ => &[did],
        };
        vouchers.iter().any(|v| trusted.iter().any(|m| s.vouched(v, m)))
    };
    match kind {
        Kind::OnlyMe => only_me,
        Kind::TangledVouch => only_me || trust_vouched(),
        Kind::Mutuals => only_me || (trust_vouched() && vouches_trusted()),
        _ => true,
    }
}

fn snapshot_for(input: &Value, reads: &mut Reads, perspective: &str, self_did: &str, cp: &str) -> Option<Snapshot> {
    if let Some(seeded) = input["snapshot"].as_object() {
        let map = |key: &str| seeded.get(key).and_then(Value::as_object).cloned().unwrap_or_default();
        return Some(Snapshot { operator_of: map("operatorOf"), vouched_by: map("vouchedBy") });
    }
    // executor/ghalite.go snapshotServices: each side's operator is resolved under its own role.
    let (self_svc, cp_svc) = if perspective == "requester" { (REQUESTER_ASSOCIATE, BIDDER_ASSOCIATE) } else { (BIDDER_ASSOCIATE, REQUESTER_ASSOCIATE) };
    let now = instant(text(input, "now"));
    build_snapshot(reads, self_did, cp, self_svc, cp_svc, now)
}

// ---- trust.BuildSnapshotWith over getRecord reads ----------------------------------------------

pub enum Rec<'a> {
    Absent,
    Failed(&'a str),
    Found(&'a Value),
}

pub struct Reads<'a> {
    got: &'a Map<String, Value>,
    need: Vec<String>,
}

impl<'a> Reads<'a> {
    // None: not read yet (now asked for).
    fn get(&mut self, uri: &str) -> Option<Rec<'a>> {
        match self.got.get(uri) {
            None => {
                if !self.need.iter().any(|u| u == uri) {
                    self.need.push(uri.to_string());
                }
                None
            }
            Some(Value::Null) => Some(Rec::Absent),
            Some(v) if v.get("$error").is_some() => Some(Rec::Failed(v["$error"].as_str().unwrap_or("call failed"))),
            Some(v) => Some(Rec::Found(v)),
        }
    }

    // atproto.Client.GetRecordAt + cid check (trust/association.go pinnedRecord): the value, or None.
    fn pinned(&mut self, uri: &str, cid: &str) -> Option<Option<&'a Value>> {
        Some(match self.get(uri)? {
            Rec::Found(output) if output["cid"] == cid => Some(&output["value"]),
            _ => None,
        })
    }
}

fn uri(repo: &str, collection: &str, rkey: &str) -> String {
    format!("at://{repo}/{collection}/{rkey}")
}

fn split(uri: &str) -> (&str, &str, &str) {
    let mut parts = uri.strip_prefix("at://").unwrap_or(uri).splitn(3, '/');
    (parts.next().unwrap_or(""), parts.next().unwrap_or(""), parts.next().unwrap_or(""))
}

fn strong_ref(value: &Value) -> Option<(&str, &str)> {
    let uri = value["uri"].as_str().filter(|u| u.starts_with("at://"))?;
    let cid = value["cid"].as_str().filter(|c| !c.is_empty())?;
    Some((uri, cid))
}

#[derive(PartialEq)]
struct Claim<'a> {
    operator: &'a str,
    subject: &'a str,
    service: &'a str,
}

// trust.go claimOf
fn claim_of(value: &Value) -> Option<Claim<'_>> {
    let field = |k: &str| value[k].as_str().map(str::trim).unwrap_or("");
    let claim = Claim { operator: field("challenge"), subject: field("keyId"), service: field("service") };
    (claim.operator.starts_with("did:") && claim.subject.starts_with("did:")).then_some(claim)
}

fn claim_rkey(service: &str, subject: &str) -> String {
    format!("{service}-{}", subject.replace(':', "-"))
}

fn sha(parts: &[&[u8]]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    for part in parts {
        hasher.update(part);
    }
    hasher.finalize().into()
}

fn hex16(sum: &[u8; 32]) -> String {
    sum[..16].iter().map(|b| format!("{b:02x}")).collect()
}

// market.ReceiptRKey, SessionRKey("delete", receipt) (ReleasePayloadRKey), EventRKeys(TerminationKey) payload rkey.
fn receipt_rkey(accept_uri: &str, accept_cid: &str) -> String {
    format!("receipt-{}", hex16(&sha(&[accept_uri.trim().as_bytes(), b"\n", accept_cid.trim().as_bytes()])))
}

fn release_rkey(receipt_uri: &str) -> String {
    const ALPHABET: &[u8] = b"234567abcdefghijklmnopqrstuvwxyz";
    let sum = sha(&[b"delete", &[0], receipt_uri.as_bytes()]);
    sum[..13].iter().map(|b| ALPHABET[*b as usize % ALPHABET.len()] as char).collect()
}

fn termination_rkey(receipt_uri: &str) -> String {
    format!("termination-payload-{}", hex16(&sha(&[b"terminated:", receipt_uri.as_bytes()])))
}

// trust.go ResolveOperatorDidWith / operatorOf: Some(Some(op)) a verified operator, Some(None) none.
fn operator(r: &mut Reads, did: &str, service: &str, now: Option<(i64, u32)>) -> Option<Option<String>> {
    let Rec::Found(mirror) = r.get(&uri(did, BADGE, "association"))? else { return Some(None) };
    let value = &mirror["value"];
    let Some(want) = claim_of(value).filter(|c| c.subject == did && c.operator != did && c.service == service) else { return Some(None) };
    let held = if let Some(proof) = value.as_object().and_then(|o| o.get("proof")) {
        proof_held(r, &want, proof, now)?
    } else {
        let association = association_held(r, &want, now);
        let claim = claim_held(r, &want);
        match (association?, claim?) {
            (Err(()), _) => false,
            (Ok(held), claim) => held || claim,
        }
    };
    Some(held.then(|| want.operator.to_string()))
}

// trust/association.go Matches for the default kinds.
fn matches(collection: &str, value: &Value, want: &Claim) -> bool {
    if !value.is_object() || want.operator.is_empty() || want.subject.is_empty() || want.operator == want.subject || value["$type"] != collection {
        return false;
    }
    match collection {
        BADGE => claim_of(value).is_some_and(|c| c == *want),
        ASSOCIATION => value["subject"] == want.subject && value["service"] == want.service,
        _ => false,
    }
}

fn proof_held(r: &mut Reads, want: &Claim, proof: &Value, now: Option<(i64, u32)>) -> Option<bool> {
    let Some((proof_uri, cid)) = strong_ref(proof) else { return Some(false) };
    let (repo, collection, _) = split(proof_uri);
    // Posts (bskyPost, delvePost) are association kinds kcp accepts only when configured; the
    // default Association{} (what executor/ghalite.go uses) accepts claim and association.
    if repo != want.operator || (collection != BADGE && collection != ASSOCIATION) {
        return Some(false);
    }
    let Some(value) = r.pinned(proof_uri, cid)? else { return Some(false) };
    if !matches(collection, value, want) {
        return Some(false);
    }
    scope_held(r, value, want, now)
}

// Err(()): the read failed, which kcp treats as no operator at all (no fallback to the claim).
fn association_held(r: &mut Reads, want: &Claim, now: Option<(i64, u32)>) -> Option<Result<bool, ()>> {
    let value = match r.get(&uri(want.operator, ASSOCIATION, &claim_rkey(want.service, want.subject)))? {
        Rec::Found(output) => &output["value"],
        Rec::Absent => return Some(Ok(false)),
        Rec::Failed(_) => return Some(Err(())),
    };
    if !matches(ASSOCIATION, value, want) {
        return Some(Ok(false));
    }
    scope_held(r, value, want, now).map(Ok)
}

fn claim_held(r: &mut Reads, want: &Claim) -> Option<bool> {
    Some(match r.get(&uri(want.operator, BADGE, &claim_rkey(want.service, want.subject)))? {
        Rec::Found(output) => claim_of(&output["value"]).is_some_and(|c| c == *want),
        _ => false,
    })
}

fn scope_held(r: &mut Reads, value: &Value, want: &Claim, now: Option<(i64, u32)>) -> Option<bool> {
    let fields = value.as_object();
    let scoped = fields.is_some_and(|f| f.contains_key("contract"));
    let expiring = fields.is_some_and(|f| f.contains_key("expiresAt"));
    let expires = value["expiresAt"].as_str().unwrap_or("");
    if scoped && expires.is_empty() {
        return Some(false);
    }
    if expiring && !matches!((now, instant(expires)), (Some(now), Some(deadline)) if now < deadline) {
        return Some(false);
    }
    if !scoped {
        return Some(true);
    }
    let Some((contract, cid)) = strong_ref(&value["contract"]) else { return Some(false) };
    contract_held(r, contract, cid, want.operator)
}

// trust/association.go contractHeld: a held, unreleased, unterminated market.accept of the operator.
fn contract_held(r: &mut Reads, contract: &str, cid: &str, operator: &str) -> Option<bool> {
    let (repo, collection, _) = split(contract);
    if repo != operator || collection != ACCEPT {
        return Some(false);
    }
    let Some(accept) = r.pinned(contract, cid)? else { return Some(false) };
    let Some((bid, _)) = strong_ref(&accept["bid"]) else { return Some(false) };
    let bidder = split(bid).0;
    if !bidder.starts_with("did:") {
        return Some(false);
    }
    let receipt_uri = uri(bidder, RECEIPT, &receipt_rkey(contract, cid));
    let Rec::Found(receipt) = r.get(&receipt_uri)? else { return Some(false) };
    if receipt["uri"] != receipt_uri.as_str() || strong_ref(&receipt["value"]["accept"]) != Some((contract, cid)) {
        return Some(false);
    }
    let released = r.get(&uri(operator, VM_DELETE, &release_rkey(&receipt_uri)));
    let terminated = r.get(&uri(bidder, VM_TERMINATED, &termination_rkey(&receipt_uri)));
    Some(matches!((released?, terminated?), (Rec::Absent, Rec::Absent)))
}

// trust.go Vouches: a vouch record (rkey = subject) that is not a denouncement.
fn vouches(r: &mut Reads, voucher: &str, subject: &str) -> Option<bool> {
    if voucher.is_empty() || !subject.starts_with("did:") {
        return Some(false);
    }
    Some(match r.get(&uri(voucher, VOUCH, subject))? {
        Rec::Found(output) => output["value"]["kind"] != "denounce",
        _ => false,
    })
}

fn distinct<'a>(values: &[&'a str]) -> Vec<&'a str> {
    let mut out: Vec<&str> = Vec::new();
    for v in values {
        if !v.is_empty() && !out.contains(v) {
            out.push(v);
        }
    }
    out
}

fn build_snapshot(r: &mut Reads, self_did: &str, cp: &str, self_svc: &str, cp_svc: &str, now: Option<(i64, u32)>) -> Option<Snapshot> {
    let self_op = operator(r, self_did, self_svc, now);
    let cp_op = if cp.is_empty() || cp == self_did { Some(None) } else { operator(r, cp, cp_svc, now) };
    let self_op = self_op?.unwrap_or_else(|| self_did.to_string());
    let cp_op = cp_op?;
    let co = cp_op.as_deref().unwrap_or("");
    let mut operator_of = Map::new();
    operator_of.insert(self_did.into(), json!(self_op));
    if !co.is_empty() {
        operator_of.insert(cp.into(), json!(co));
    }
    let trusted = distinct(&[self_did, &self_op]);
    let vouchers = distinct(&[&trusted[..], &[cp, co]].concat());
    let subjects = distinct(&[self_did, &self_op, cp, co]);
    let mut pending = false;
    let mut vouched_by = Map::new();
    for voucher in &vouchers {
        let mut list = Vec::new();
        for subject in subjects.iter().filter(|s| *s != voucher) {
            match vouches(r, voucher, subject) {
                None => pending = true,
                Some(true) => list.push(json!(subject)),
                Some(false) => {}
            }
        }
        vouched_by.insert(voucher.to_string(), Value::Array(list));
    }
    (!pending).then_some(Snapshot { operator_of, vouched_by })
}

// RFC 3339 (time.RFC3339Nano) to (unix seconds, nanoseconds).
fn instant(s: &str) -> Option<(i64, u32)> {
    let b = s.as_bytes();
    let num = |from: usize, to: usize| -> Option<i64> { s.get(from..to)?.parse().ok().filter(|_| b[from..to].iter().all(u8::is_ascii_digit)) };
    if b.len() < 20 || b[4] != b'-' || b[7] != b'-' || b[10] != b'T' || b[13] != b':' || b[16] != b':' {
        return None;
    }
    let (y, mo, d, h, mi, sec) = (num(0, 4)?, num(5, 7)?, num(8, 10)?, num(11, 13)?, num(14, 16)?, num(17, 19)?);
    if !(1..=12).contains(&mo) || !(1..=31).contains(&d) || h > 23 || mi > 59 || sec > 59 {
        return None;
    }
    let mut i = 19;
    let mut nanos = 0u32;
    if b[i] == b'.' {
        let start = i + 1;
        i = start;
        while i < b.len() && b[i].is_ascii_digit() {
            i += 1;
        }
        if i == start || i - start > 9 {
            return None;
        }
        nanos = format!("{:0<9}", &s[start..i]).parse().ok()?;
    }
    let offset = match b.get(i)? {
        b'Z' if i + 1 == b.len() => 0,
        sign @ (b'+' | b'-') if i + 6 == b.len() && b[i + 3] == b':' => {
            let minutes = num(i + 1, i + 3)? * 60 + num(i + 4, i + 6)?;
            if *sign == b'+' { minutes * 60 } else { -minutes * 60 }
        }
        _ => return None,
    };
    let yy = if mo <= 2 { y - 1 } else { y };
    let era = yy.div_euclid(400);
    let yoe = yy - era * 400;
    let doy = (153 * ((mo + 9) % 12) + 2) / 5 + d - 1;
    let days = era * 146097 + yoe * 365 + yoe / 4 - yoe / 100 + doy - 719468;
    Some((days * 86400 + h * 3600 + mi * 60 + sec - offset, nanos))
}