Advance forge rollout, Ethereum rails, and NBC sources
This commit is contained in:
parent
be26313225
commit
7d84510eac
88 changed files with 11230 additions and 302 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -22,6 +22,10 @@ third_party/iroh-org/*
|
|||
|
||||
# Cloudflare worker local deps / builds
|
||||
deploy/cloudflare-worker/node_modules/
|
||||
cache/
|
||||
out/
|
||||
test-results/
|
||||
.tower-minimal/
|
||||
|
||||
# NEVER commit private keys
|
||||
every_channel_ed25519
|
||||
|
|
|
|||
924
Cargo.lock
generated
924
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"crates/ec-core",
|
||||
"crates/ec-eth",
|
||||
"crates/ec-moq",
|
||||
"crates/ec-direct",
|
||||
"crates/ec-hdhomerun",
|
||||
|
|
|
|||
77
README.md
77
README.md
|
|
@ -16,6 +16,7 @@ A global, disaggregated mesh of relays that turns local ATSC antennas into a coh
|
|||
- `crates/ec-linux-iptv`: Linux DVB ingest scaffolding.
|
||||
- `crates/ec-iroh`: iroh transport scaffolding.
|
||||
- `crates/ec-crypto`: stream key derivation helpers.
|
||||
- `crates/ec-eth`: Ethereum-compatible protocol representations and commitments.
|
||||
- `crates/ec-ts`: MPEG-TS timing and table parsing.
|
||||
- `crates/ec-chopper`: deterministic ffmpeg chunking scaffolding.
|
||||
- `crates/ec-moq`: MoQ data model and relay scaffolding.
|
||||
|
|
@ -26,6 +27,7 @@ A global, disaggregated mesh of relays that turns local ATSC antennas into a coh
|
|||
- `docs/USAGE.md`: runbook for viewer and ingest pipelines.
|
||||
- `docs/IROH_EXAMPLES.md`: summary of iroh repos/examples used for design.
|
||||
- `docs/`: architecture, roadmap, and MoQ notes.
|
||||
- `contracts/`: Solidity contracts for compact private-chain settlement rails.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
@ -53,7 +55,19 @@ Git hosting topology:
|
|||
cat docs/GIT_HOSTING.md
|
||||
```
|
||||
|
||||
NUC PXE rollout (Unifi + ProxyDHCP):
|
||||
Sovereign forge deploy:
|
||||
|
||||
```sh
|
||||
cat docs/DEPLOY_ECP_FORGE.md
|
||||
```
|
||||
|
||||
OP Stack `ecp-forge` runbook:
|
||||
|
||||
```sh
|
||||
cat docs/OP_STACK_ECP_FORGE.md
|
||||
```
|
||||
|
||||
NUC PXE rollout (UniFi-only or ProxyDHCP):
|
||||
|
||||
```sh
|
||||
cat docs/NUC_UNIFI_NETBOOT.md
|
||||
|
|
@ -129,6 +143,67 @@ cargo run -p ec-node -- control-bridge-web \
|
|||
`control endpoint id` and `control endpoint addr` on startup. Use the `endpoint addr` JSON for
|
||||
`--gossip-peer` when bootstrapping.
|
||||
|
||||
Default discovery identity:
|
||||
|
||||
- If you pass `--stream-id`, that exact value is announced.
|
||||
- If you do not pass `--stream-id`, `ec-node` now prefers a broadcast-scoped ID when the selected
|
||||
channel exposes usable broadcast metadata. HDHomeRun sources also probe early TS packets to fill
|
||||
missing `tsid` / `program_number`, and raw TS inputs can derive identity directly from PAT data.
|
||||
- ATSC PSIP parsing now covers `MGT`, `TVCT/CVCT`, `STT`, `RRT`, `EIT`, and `ETT`. `EIT` and `ETT`
|
||||
are accepted on the PIDs advertised by `MGT`, not only on the base PSIP PID `0x1FFB`.
|
||||
- Discovery identity currently consumes `PAT` plus `VCT` data. `RRT`, `EIT`, `ETT`, and full `STT`
|
||||
parsing are available for inspection and future policy, but they do not currently change the
|
||||
default dedupe key beyond the existing broadcast identity fields.
|
||||
- PAT-derived promotion is intentionally conservative: only single-program streams are promoted. If
|
||||
the probe sees multiple non-zero programs, `ec-node` does not guess and keeps the stream
|
||||
source-scoped.
|
||||
- Sources without usable broadcast metadata still fall back to source-scoped IDs.
|
||||
|
||||
Ethereum rails:
|
||||
|
||||
- `ec-node` keeps BLAKE3 `manifest_id`s and `merkle+blake3` object proofs on the wire, and also
|
||||
emits Ethereum-compatible ABI commitments for settlement.
|
||||
- Manifests can now carry both local Ed25519 signatures and optional secp256k1 EIP-712 signatures
|
||||
over `ManifestBody`. Set `EVERY_CHANNEL_MANIFEST_SIGNING_KEY` for the local signer and
|
||||
`EVERY_CHANNEL_ETH_MANIFEST_SIGNING_KEY` for the Ethereum-native signer.
|
||||
- Stream descriptors and iroh control announcements now carry Keccak commitments so broadcast
|
||||
identity, discovery, and manifest settlement share one vocabulary.
|
||||
- The new `contracts/EveryChannelRail.sol` contract is intentionally storage-light: it stores only
|
||||
latest commitment pointers, while full manifests and media stay off-chain on iroh/relays/archive.
|
||||
- Observation consensus now has dedicated Solidity surfaces:
|
||||
`contracts/EveryChannelWitnessRegistry.sol` and
|
||||
`contracts/EveryChannelObservationLedger.sol`.
|
||||
- `scripts/op-stack/anvil-reality-smoke.sh` validates the observation rail against real archive
|
||||
data pulled from `ecp-forge`, not only synthetic fixtures.
|
||||
- `nix/modules/ec-op-stack.nix` and `docs/OP_STACK_ECP_FORGE.md` define the repo-owned OP Stack
|
||||
operator path for `ecp-forge`.
|
||||
- The dev shell now includes `forge`, `cast`, `anvil`, and `solc`, and the repo ships a
|
||||
top-level `foundry.toml`.
|
||||
|
||||
Real-capture PSIP checks:
|
||||
|
||||
```sh
|
||||
git clone --depth 1 https://github.com/tsduck/tsduck-test /tmp/tsduck-test
|
||||
EC_REAL_ATSC_SAMPLE=/tmp/tsduck-test/input/test-040.ts \
|
||||
cargo test -p ec-ts real_atsc_sample_matches_known_psip_shape -- --ignored --nocapture
|
||||
EC_REAL_RRT_SAMPLE=/tmp/tsduck-test/input/test-052.ts \
|
||||
cargo test -p ec-ts real_rrt_sample_matches_known_reference -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Ethereum commitment checks:
|
||||
|
||||
```sh
|
||||
cargo test -p ec-eth -p ec-node -- --nocapture
|
||||
nix develop -c bash -lc 'which forge cast anvil solc && solc --bin contracts/EveryChannelRail.sol >/dev/null'
|
||||
```
|
||||
|
||||
Observation rail / OP Stack checks:
|
||||
|
||||
```sh
|
||||
nix shell .#foundry .#solc -c forge test -vv
|
||||
nix shell .#foundry .#solc nixpkgs#jq nixpkgs#openssh nixpkgs#curl -c ./scripts/op-stack/anvil-reality-smoke.sh
|
||||
```
|
||||
|
||||
Coverage:
|
||||
|
||||
```sh
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ blake3.workspace = true
|
|||
ec-crypto = { path = "../../crates/ec-crypto" }
|
||||
ec-core = { path = "../../crates/ec-core" }
|
||||
ec-chopper = { path = "../../crates/ec-chopper" }
|
||||
ec-eth = { path = "../../crates/ec-eth" }
|
||||
ec-hdhomerun = { path = "../../crates/ec-hdhomerun" }
|
||||
ec-linux-iptv = { path = "../../crates/ec-linux-iptv" }
|
||||
ec-iroh = { path = "../../crates/ec-iroh" }
|
||||
ec-moq = { path = "../../crates/ec-moq" }
|
||||
headless_chrome = "1"
|
||||
hex = "0.4"
|
||||
iroh = "0.96"
|
||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] }
|
||||
|
|
@ -25,5 +27,16 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
|||
tower-http = { version = "0.5", features = ["fs"] }
|
||||
tracing.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
block2 = "0.6"
|
||||
objc2 = "0.6"
|
||||
objc2-app-kit = { version = "0.3", features = ["NSBitmapImageRep", "NSImage"] }
|
||||
objc2-core-foundation = "0.3"
|
||||
objc2-foundation = { version = "0.3", features = ["NSData", "NSDictionary", "NSError", "NSString"] }
|
||||
objc2-web-kit = { version = "0.3", features = ["WKSnapshotConfiguration", "WKWebView", "block2", "objc2-app-kit"] }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[features]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,3 @@
|
|||
[build]
|
||||
dist = "../dist"
|
||||
public_url = "/"
|
||||
public_url = "./"
|
||||
|
|
|
|||
|
|
@ -20,8 +20,80 @@
|
|||
<link data-trunk rel="copy-dir" href="icons" />
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="boot-status"
|
||||
style="
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
background: #f7f4ef;
|
||||
color: #2b241d;
|
||||
font: 15px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
white-space: pre-wrap;
|
||||
z-index: 9999;
|
||||
"
|
||||
>
|
||||
Loading every.channel…
|
||||
</div>
|
||||
<div id="main"></div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const boot = document.getElementById("boot-status");
|
||||
const main = document.getElementById("main");
|
||||
if (!boot || !main) return;
|
||||
|
||||
const show = (message) => {
|
||||
boot.textContent = message;
|
||||
boot.style.display = "flex";
|
||||
};
|
||||
|
||||
const hide = () => {
|
||||
boot.remove();
|
||||
};
|
||||
|
||||
const mounted = () => {
|
||||
if (main.childElementCount > 0) {
|
||||
hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
new MutationObserver(() => {
|
||||
mounted();
|
||||
}).observe(main, { childList: true, subtree: true });
|
||||
|
||||
window.addEventListener("error", (event) => {
|
||||
const message =
|
||||
event?.error?.stack ||
|
||||
event?.error?.message ||
|
||||
event?.message ||
|
||||
"Unknown frontend boot error";
|
||||
show(`Frontend boot error\n\n${message}`);
|
||||
});
|
||||
|
||||
window.addEventListener("unhandledrejection", (event) => {
|
||||
const reason = event?.reason;
|
||||
const message =
|
||||
reason?.stack ||
|
||||
reason?.message ||
|
||||
(typeof reason === "string" ? reason : JSON.stringify(reason, null, 2)) ||
|
||||
"Unknown unhandled rejection";
|
||||
show(`Frontend boot rejection\n\n${message}`);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (!mounted()) {
|
||||
show("Loading every.channel is taking longer than expected…");
|
||||
}
|
||||
}, 4000);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Installable app shell (PWA). Keep this tiny and resilient.
|
||||
if ("serviceWorker" in navigator) {
|
||||
|
|
|
|||
|
|
@ -130,6 +130,24 @@ struct ProbeStreamArgs {
|
|||
input: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BootstrapNbcAuthArgs {
|
||||
input: Option<String>,
|
||||
stream_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BootstrapNbcAuthResult {
|
||||
input_url: String,
|
||||
stream_id: Option<String>,
|
||||
hidden_mode: bool,
|
||||
surfaced_auth: bool,
|
||||
data_dir: Option<String>,
|
||||
status: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ManualSourceOptions {
|
||||
|
|
@ -777,6 +795,13 @@ fn App() -> Element {
|
|||
let active_id = selected.read().as_ref().map(|s| s.id.clone());
|
||||
let playback_url = playback.read().as_ref().map(|p| p.url.clone());
|
||||
let now_playing = selected.read().clone();
|
||||
let bootstrap_stream = selected
|
||||
.read()
|
||||
.as_ref()
|
||||
.filter(|stream| stream_source_kind(stream) == "nbc")
|
||||
.cloned();
|
||||
let bootstrap_input_value = add_input.read().clone();
|
||||
let bootstrap_input_is_nbc = looks_like_nbc_input(&bootstrap_input_value);
|
||||
let current_share = share_info.read().clone();
|
||||
let source_list = sources.read().clone();
|
||||
|
||||
|
|
@ -894,6 +919,82 @@ fn App() -> Element {
|
|||
onclick: move |_| refresh_sources(),
|
||||
"Refresh"
|
||||
}
|
||||
if bootstrap_stream.is_some() || bootstrap_input_is_nbc {
|
||||
div { class: "source-menu-divider" }
|
||||
div { class: "source-menu-section",
|
||||
div { class: "source-menu-title", "NBC auth" }
|
||||
div { class: "source-menu-status",
|
||||
"Warm a hidden NBC session. The window only appears if MVPD auth is needed."
|
||||
}
|
||||
if let Some(stream) = bootstrap_stream.clone() {
|
||||
button {
|
||||
class: "source-menu-action",
|
||||
onclick: move |_| {
|
||||
if !tauri_available() {
|
||||
status.set("Tauri backend not available (open the Tauri app)".to_string());
|
||||
return;
|
||||
}
|
||||
let stream_id = stream.id.clone();
|
||||
let stream_title = stream.title.clone();
|
||||
let args = BootstrapNbcAuthArgs {
|
||||
input: None,
|
||||
stream_id: Some(stream_id),
|
||||
};
|
||||
let mut status = status.clone();
|
||||
spawn(async move {
|
||||
status.set(format!("Bootstrapping NBC auth for {}", stream_title));
|
||||
match tauri_invoke::<BootstrapNbcAuthResult, _>("bootstrap_nbc_auth", &args).await {
|
||||
Ok(result) => {
|
||||
if result.surfaced_auth {
|
||||
status.set("NBC auth ready after interactive sign-in".to_string());
|
||||
} else {
|
||||
status.set("NBC auth ready in hidden mode".to_string());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
status.set(format!("NBC bootstrap error: {err}"));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
"Bootstrap selected NBC"
|
||||
}
|
||||
}
|
||||
if bootstrap_input_is_nbc {
|
||||
button {
|
||||
class: "source-menu-action",
|
||||
onclick: move |_| {
|
||||
if !tauri_available() {
|
||||
status.set("Tauri backend not available (open the Tauri app)".to_string());
|
||||
return;
|
||||
}
|
||||
let input = bootstrap_input_value.clone();
|
||||
let args = BootstrapNbcAuthArgs {
|
||||
input: Some(input.clone()),
|
||||
stream_id: None,
|
||||
};
|
||||
let mut status = status.clone();
|
||||
spawn(async move {
|
||||
status.set(format!("Bootstrapping NBC auth for {}", input));
|
||||
match tauri_invoke::<BootstrapNbcAuthResult, _>("bootstrap_nbc_auth", &args).await {
|
||||
Ok(result) => {
|
||||
if result.surfaced_auth {
|
||||
status.set("NBC auth ready after interactive sign-in".to_string());
|
||||
} else {
|
||||
status.set("NBC auth ready in hidden mode".to_string());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
status.set(format!("NBC bootstrap error: {err}"));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
"Bootstrap pasted NBC URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div { class: "source-menu-divider" }
|
||||
div { class: "source-menu-section",
|
||||
div { class: "source-menu-title", "Add stream" }
|
||||
|
|
@ -1707,6 +1808,7 @@ fn App() -> Element {
|
|||
option { value: "linux-dvb", "Linux DVB" }
|
||||
option { value: "hls", "HLS" }
|
||||
option { value: "ytdlp", "yt-dlp" }
|
||||
option { value: "nbc", "NBC" }
|
||||
option { value: "moq", "Link" }
|
||||
}
|
||||
button {
|
||||
|
|
@ -1753,6 +1855,8 @@ fn App() -> Element {
|
|||
.map(|id| id == &stream.id)
|
||||
.unwrap_or(false);
|
||||
let card_class = if is_active { "channel-card active" } else { "channel-card" };
|
||||
let channel_subtitle = stream_display_subtitle(stream);
|
||||
let channel_detail = stream_display_detail(stream);
|
||||
let moq_endpoint = stream
|
||||
.metadata
|
||||
.iter()
|
||||
|
|
@ -1835,8 +1939,11 @@ fn App() -> Element {
|
|||
});
|
||||
},
|
||||
div { class: "channel-title", "{stream.title}" }
|
||||
div { class: "channel-meta",
|
||||
{stream.number.clone().unwrap_or_default()}
|
||||
if let Some(channel_subtitle) = channel_subtitle.clone() {
|
||||
div { class: "channel-meta", "{channel_subtitle}" }
|
||||
}
|
||||
if let Some(channel_detail) = channel_detail.clone() {
|
||||
div { class: "channel-detail", "{channel_detail}" }
|
||||
}
|
||||
if !stream.source.is_empty() {
|
||||
div { class: "channel-badge source", "{stream.source}" }
|
||||
|
|
@ -2293,6 +2400,65 @@ fn stream_has_drm(metadata: &[StreamMetadata]) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
fn stream_metadata_value<'a>(stream: &'a StreamDescriptor, key: &str) -> Option<&'a str> {
|
||||
stream
|
||||
.metadata
|
||||
.iter()
|
||||
.find(|entry| entry.key == key)
|
||||
.map(|entry| entry.value.trim())
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn stream_display_subtitle(stream: &StreamDescriptor) -> Option<String> {
|
||||
if stream_source_kind(stream) == "nbc" {
|
||||
if let Some(program) =
|
||||
stream_metadata_value(stream, "current_program").or_else(|| stream.number.as_deref())
|
||||
{
|
||||
return Some(format!("Now: {}", program.trim()));
|
||||
}
|
||||
if let Some(brand) = stream_metadata_value(stream, "nbc_brand") {
|
||||
return Some(brand.to_string());
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
stream
|
||||
.number
|
||||
.as_ref()
|
||||
.map(|value| value.trim())
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| value.to_string())
|
||||
}
|
||||
|
||||
fn stream_display_detail(stream: &StreamDescriptor) -> Option<String> {
|
||||
if stream_source_kind(stream) != "nbc" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut parts = Vec::new();
|
||||
if let Some(badge) = stream_metadata_value(stream, "badge") {
|
||||
parts.push(badge.to_string());
|
||||
}
|
||||
if let Some(entitlement) = stream_metadata_value(stream, "entitlement") {
|
||||
if !entitlement.eq_ignore_ascii_case("free") {
|
||||
parts.push("Adobe auth".to_string());
|
||||
}
|
||||
} else if stream_has_drm(&stream.metadata) {
|
||||
parts.push("Adobe auth".to_string());
|
||||
}
|
||||
|
||||
if parts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(parts.join(" • "))
|
||||
}
|
||||
}
|
||||
|
||||
fn looks_like_nbc_input(value: &str) -> bool {
|
||||
let value = value.trim().to_ascii_lowercase();
|
||||
value.starts_with("https://www.nbc.com/") || value.starts_with("https://nbc.com/")
|
||||
}
|
||||
|
||||
fn stream_source_kind(stream: &StreamDescriptor) -> String {
|
||||
let source = stream.source.trim();
|
||||
if !source.is_empty() {
|
||||
|
|
|
|||
|
|
@ -392,6 +392,13 @@ body::before {
|
|||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.channel-detail {
|
||||
font-size: 11px;
|
||||
color: var(--ink-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.source-status {
|
||||
font-size: 13px;
|
||||
color: var(--ink-muted);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const HLS_MODULE_URLS = [
|
|||
"https://unpkg.com/hls.js@1.6.2/dist/hls.mjs",
|
||||
];
|
||||
const PUBLIC_STREAMS_PATH = "/api/public-streams";
|
||||
const LIVE_JITTER_MS = 750;
|
||||
let moqWatchModulePromise = null;
|
||||
let hlsModulePromise = null;
|
||||
let disposePlayerSignals = null;
|
||||
|
|
@ -163,6 +164,7 @@ function mountPlayer(relayUrl, name) {
|
|||
watch.setAttribute("path", name);
|
||||
watch.setAttribute("volume", "1");
|
||||
watch.setAttribute("muted", "");
|
||||
watch.setAttribute("jitter", String(LIVE_JITTER_MS));
|
||||
|
||||
// Force WebTransport in-browser; websocket fallback has shown degraded
|
||||
// media behavior (especially audio) against public relay paths.
|
||||
|
|
|
|||
193
contracts/EveryChannelObservationLedger.sol
Normal file
193
contracts/EveryChannelObservationLedger.sol
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import {EveryChannelWitnessRegistry} from "./EveryChannelWitnessRegistry.sol";
|
||||
|
||||
/// @title EveryChannelObservationLedger
|
||||
/// @notice Observation consensus ledger for reality-derived every.channel epochs.
|
||||
/// Witnesses attest to the same derived observation hash, and the first candidate to hit quorum
|
||||
/// finalizes the `(stream, epoch)` slot.
|
||||
contract EveryChannelObservationLedger {
|
||||
struct ObservationHeader {
|
||||
bytes32 streamHash;
|
||||
bytes32 epochHash;
|
||||
bytes32 parentObservationHash;
|
||||
bytes32 dataRoot;
|
||||
bytes32 locatorHash;
|
||||
uint64 observedUnixMs;
|
||||
uint64 sequence;
|
||||
}
|
||||
|
||||
struct Candidate {
|
||||
bytes32 slot;
|
||||
address proposer;
|
||||
uint64 observedUnixMs;
|
||||
uint64 sequence;
|
||||
uint32 attestations;
|
||||
bool finalized;
|
||||
}
|
||||
|
||||
EveryChannelWitnessRegistry public immutable witnessRegistry;
|
||||
uint256 public immutable quorum;
|
||||
|
||||
mapping(bytes32 => ObservationHeader) private observationHeaders;
|
||||
mapping(bytes32 => Candidate) public candidates;
|
||||
mapping(bytes32 => bytes32) public finalizedObservationBySlot;
|
||||
mapping(bytes32 => mapping(address => bool)) public hasAttested;
|
||||
|
||||
event ObservationProposed(
|
||||
bytes32 indexed slot,
|
||||
bytes32 indexed observationHash,
|
||||
address indexed proposer,
|
||||
bytes32 streamHash,
|
||||
bytes32 epochHash,
|
||||
bytes32 parentObservationHash,
|
||||
bytes32 dataRoot,
|
||||
bytes32 locatorHash,
|
||||
uint64 observedUnixMs,
|
||||
uint64 sequence
|
||||
);
|
||||
|
||||
event ObservationAttested(
|
||||
bytes32 indexed slot,
|
||||
bytes32 indexed observationHash,
|
||||
address indexed witness,
|
||||
uint32 attestations
|
||||
);
|
||||
|
||||
event ObservationFinalized(
|
||||
bytes32 indexed slot,
|
||||
bytes32 indexed observationHash,
|
||||
uint32 attestations
|
||||
);
|
||||
|
||||
error NotWitness(address caller);
|
||||
error InvalidRegistry(address registry);
|
||||
error InvalidQuorum(uint256 quorum);
|
||||
error UnknownObservation(bytes32 observationHash);
|
||||
error ObservationAlreadyExists(bytes32 observationHash);
|
||||
error ObservationSlotAlreadyFinalized(bytes32 slot, bytes32 observationHash);
|
||||
error DuplicateAttestation(bytes32 observationHash, address witness);
|
||||
|
||||
constructor(address registry, uint256 quorumThreshold) {
|
||||
if (registry == address(0)) revert InvalidRegistry(registry);
|
||||
if (quorumThreshold == 0) revert InvalidQuorum(quorumThreshold);
|
||||
witnessRegistry = EveryChannelWitnessRegistry(registry);
|
||||
quorum = quorumThreshold;
|
||||
}
|
||||
|
||||
modifier onlyWitness() {
|
||||
if (!witnessRegistry.isWitness(msg.sender)) revert NotWitness(msg.sender);
|
||||
_;
|
||||
}
|
||||
|
||||
function observationSlot(
|
||||
bytes32 streamHash,
|
||||
bytes32 epochHash
|
||||
) public pure returns (bytes32) {
|
||||
return keccak256(abi.encode(streamHash, epochHash));
|
||||
}
|
||||
|
||||
function hashObservationHeader(
|
||||
ObservationHeader memory header
|
||||
) public pure returns (bytes32) {
|
||||
return keccak256(
|
||||
abi.encode(
|
||||
header.streamHash,
|
||||
header.epochHash,
|
||||
header.parentObservationHash,
|
||||
header.dataRoot,
|
||||
header.locatorHash,
|
||||
header.observedUnixMs,
|
||||
header.sequence
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getObservationHeader(
|
||||
bytes32 observationHash
|
||||
) external view returns (ObservationHeader memory) {
|
||||
if (candidates[observationHash].slot == bytes32(0)) {
|
||||
revert UnknownObservation(observationHash);
|
||||
}
|
||||
return observationHeaders[observationHash];
|
||||
}
|
||||
|
||||
function proposeObservation(
|
||||
ObservationHeader calldata header
|
||||
) external returns (bytes32 observationHash) {
|
||||
observationHash = hashObservationHeader(header);
|
||||
bytes32 slot = observationSlot(header.streamHash, header.epochHash);
|
||||
bytes32 finalized = finalizedObservationBySlot[slot];
|
||||
if (finalized != bytes32(0) && finalized != observationHash) {
|
||||
revert ObservationSlotAlreadyFinalized(slot, finalized);
|
||||
}
|
||||
if (candidates[observationHash].slot != bytes32(0)) {
|
||||
revert ObservationAlreadyExists(observationHash);
|
||||
}
|
||||
|
||||
observationHeaders[observationHash] = header;
|
||||
candidates[observationHash] = Candidate({
|
||||
slot: slot,
|
||||
proposer: msg.sender,
|
||||
observedUnixMs: header.observedUnixMs,
|
||||
sequence: header.sequence,
|
||||
attestations: 0,
|
||||
finalized: false
|
||||
});
|
||||
|
||||
emit ObservationProposed(
|
||||
slot,
|
||||
observationHash,
|
||||
msg.sender,
|
||||
header.streamHash,
|
||||
header.epochHash,
|
||||
header.parentObservationHash,
|
||||
header.dataRoot,
|
||||
header.locatorHash,
|
||||
header.observedUnixMs,
|
||||
header.sequence
|
||||
);
|
||||
|
||||
if (witnessRegistry.isWitness(msg.sender)) {
|
||||
_attest(observationHash, msg.sender);
|
||||
}
|
||||
}
|
||||
|
||||
function attestObservation(bytes32 observationHash) external onlyWitness {
|
||||
_attest(observationHash, msg.sender);
|
||||
}
|
||||
|
||||
function _attest(bytes32 observationHash, address witness) internal {
|
||||
Candidate storage candidate = candidates[observationHash];
|
||||
if (candidate.slot == bytes32(0)) revert UnknownObservation(observationHash);
|
||||
if (hasAttested[observationHash][witness]) {
|
||||
revert DuplicateAttestation(observationHash, witness);
|
||||
}
|
||||
|
||||
bytes32 finalized = finalizedObservationBySlot[candidate.slot];
|
||||
if (finalized != bytes32(0) && finalized != observationHash) {
|
||||
revert ObservationSlotAlreadyFinalized(candidate.slot, finalized);
|
||||
}
|
||||
|
||||
hasAttested[observationHash][witness] = true;
|
||||
candidate.attestations += 1;
|
||||
|
||||
emit ObservationAttested(
|
||||
candidate.slot,
|
||||
observationHash,
|
||||
witness,
|
||||
candidate.attestations
|
||||
);
|
||||
|
||||
if (!candidate.finalized && candidate.attestations >= quorum) {
|
||||
candidate.finalized = true;
|
||||
finalizedObservationBySlot[candidate.slot] = observationHash;
|
||||
emit ObservationFinalized(
|
||||
candidate.slot,
|
||||
observationHash,
|
||||
candidate.attestations
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
contracts/EveryChannelRail.sol
Normal file
106
contracts/EveryChannelRail.sol
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
/// @title EveryChannelRail
|
||||
/// @notice Storage-light Ethereum rails for every.channel stream identity, manifest, and
|
||||
/// transport attestations. The intended deployment target is a private Ethereum-compatible
|
||||
/// network where iroh remains the transport/control plane and this contract carries compact
|
||||
/// settlement metadata only.
|
||||
contract EveryChannelRail {
|
||||
struct ManifestPointer {
|
||||
bytes32 streamIdHash;
|
||||
bytes32 epochIdHash;
|
||||
bytes32 manifestId;
|
||||
bytes32 bodyCommitment;
|
||||
bytes32 dataRoot;
|
||||
uint64 createdUnixMs;
|
||||
address announcer;
|
||||
}
|
||||
|
||||
struct AnnouncementPointer {
|
||||
bytes32 streamIdHash;
|
||||
bytes32 announcementCommitment;
|
||||
uint64 updatedUnixMs;
|
||||
uint64 ttlMs;
|
||||
address announcer;
|
||||
}
|
||||
|
||||
mapping(bytes32 => ManifestPointer) public latestManifestByEpoch;
|
||||
mapping(bytes32 => AnnouncementPointer) public latestAnnouncementByStream;
|
||||
|
||||
event ManifestCommitted(
|
||||
bytes32 indexed streamIdHash,
|
||||
bytes32 indexed epochIdHash,
|
||||
bytes32 indexed manifestId,
|
||||
bytes32 bodyCommitment,
|
||||
bytes32 dataRoot,
|
||||
uint64 createdUnixMs,
|
||||
address announcer
|
||||
);
|
||||
|
||||
event TransportAnnounced(
|
||||
bytes32 indexed streamIdHash,
|
||||
bytes32 indexed announcementCommitment,
|
||||
uint64 updatedUnixMs,
|
||||
uint64 ttlMs,
|
||||
address announcer
|
||||
);
|
||||
|
||||
function commitManifest(
|
||||
string calldata streamId,
|
||||
string calldata epochId,
|
||||
bytes32 manifestId,
|
||||
bytes32 bodyCommitment,
|
||||
bytes32 dataRoot,
|
||||
uint64 createdUnixMs
|
||||
) external {
|
||||
bytes32 streamIdHash = keccak256(bytes(streamId));
|
||||
bytes32 epochIdHash = keccak256(bytes(epochId));
|
||||
bytes32 slot = keccak256(abi.encode(streamIdHash, epochIdHash));
|
||||
|
||||
latestManifestByEpoch[slot] = ManifestPointer({
|
||||
streamIdHash: streamIdHash,
|
||||
epochIdHash: epochIdHash,
|
||||
manifestId: manifestId,
|
||||
bodyCommitment: bodyCommitment,
|
||||
dataRoot: dataRoot,
|
||||
createdUnixMs: createdUnixMs,
|
||||
announcer: msg.sender
|
||||
});
|
||||
|
||||
emit ManifestCommitted(
|
||||
streamIdHash,
|
||||
epochIdHash,
|
||||
manifestId,
|
||||
bodyCommitment,
|
||||
dataRoot,
|
||||
createdUnixMs,
|
||||
msg.sender
|
||||
);
|
||||
}
|
||||
|
||||
function announceTransport(
|
||||
string calldata streamId,
|
||||
bytes32 announcementCommitment,
|
||||
uint64 updatedUnixMs,
|
||||
uint64 ttlMs
|
||||
) external {
|
||||
bytes32 streamIdHash = keccak256(bytes(streamId));
|
||||
|
||||
latestAnnouncementByStream[streamIdHash] = AnnouncementPointer({
|
||||
streamIdHash: streamIdHash,
|
||||
announcementCommitment: announcementCommitment,
|
||||
updatedUnixMs: updatedUnixMs,
|
||||
ttlMs: ttlMs,
|
||||
announcer: msg.sender
|
||||
});
|
||||
|
||||
emit TransportAnnounced(
|
||||
streamIdHash,
|
||||
announcementCommitment,
|
||||
updatedUnixMs,
|
||||
ttlMs,
|
||||
msg.sender
|
||||
);
|
||||
}
|
||||
}
|
||||
53
contracts/EveryChannelWitnessRegistry.sol
Normal file
53
contracts/EveryChannelWitnessRegistry.sol
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
/// @title EveryChannelWitnessRegistry
|
||||
/// @notice Minimal registry-backed witness set for observation consensus. This is intentionally
|
||||
/// simple for the first testnet tranche: explicit membership, no staking, no slashing.
|
||||
contract EveryChannelWitnessRegistry {
|
||||
address public owner;
|
||||
mapping(address => bool) public isWitness;
|
||||
uint256 public witnessCount;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
event WitnessAdded(address indexed witness);
|
||||
event WitnessRemoved(address indexed witness);
|
||||
|
||||
error NotOwner();
|
||||
error ZeroAddress();
|
||||
error WitnessAlreadyRegistered(address witness);
|
||||
error WitnessNotRegistered(address witness);
|
||||
|
||||
constructor(address initialOwner) {
|
||||
if (initialOwner == address(0)) revert ZeroAddress();
|
||||
owner = initialOwner;
|
||||
emit OwnershipTransferred(address(0), initialOwner);
|
||||
}
|
||||
|
||||
modifier onlyOwner() {
|
||||
if (msg.sender != owner) revert NotOwner();
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner) external onlyOwner {
|
||||
if (newOwner == address(0)) revert ZeroAddress();
|
||||
address previous = owner;
|
||||
owner = newOwner;
|
||||
emit OwnershipTransferred(previous, newOwner);
|
||||
}
|
||||
|
||||
function addWitness(address witness) external onlyOwner {
|
||||
if (witness == address(0)) revert ZeroAddress();
|
||||
if (isWitness[witness]) revert WitnessAlreadyRegistered(witness);
|
||||
isWitness[witness] = true;
|
||||
witnessCount += 1;
|
||||
emit WitnessAdded(witness);
|
||||
}
|
||||
|
||||
function removeWitness(address witness) external onlyOwner {
|
||||
if (!isWitness[witness]) revert WitnessNotRegistered(witness);
|
||||
isWitness[witness] = false;
|
||||
witnessCount -= 1;
|
||||
emit WitnessRemoved(witness);
|
||||
}
|
||||
}
|
||||
158
contracts/test/ObservationLedger.t.sol
Normal file
158
contracts/test/ObservationLedger.t.sol
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import {EveryChannelObservationLedger} from "../EveryChannelObservationLedger.sol";
|
||||
import {EveryChannelWitnessRegistry} from "../EveryChannelWitnessRegistry.sol";
|
||||
|
||||
contract WitnessActor {
|
||||
function propose(
|
||||
EveryChannelObservationLedger ledger,
|
||||
EveryChannelObservationLedger.ObservationHeader calldata header
|
||||
) external returns (bytes32) {
|
||||
return ledger.proposeObservation(header);
|
||||
}
|
||||
|
||||
function attest(
|
||||
EveryChannelObservationLedger ledger,
|
||||
bytes32 observationHash
|
||||
) external {
|
||||
ledger.attestObservation(observationHash);
|
||||
}
|
||||
}
|
||||
|
||||
contract ObservationLedgerTest {
|
||||
function _header(
|
||||
bytes32 streamHash,
|
||||
bytes32 epochHash,
|
||||
uint64 sequence,
|
||||
bytes32 dataRoot
|
||||
) internal pure returns (EveryChannelObservationLedger.ObservationHeader memory) {
|
||||
return EveryChannelObservationLedger.ObservationHeader({
|
||||
streamHash: streamHash,
|
||||
epochHash: epochHash,
|
||||
parentObservationHash: bytes32(0),
|
||||
dataRoot: dataRoot,
|
||||
locatorHash: keccak256(abi.encodePacked(streamHash, epochHash, sequence)),
|
||||
observedUnixMs: 1_772_001_256_329,
|
||||
sequence: sequence
|
||||
});
|
||||
}
|
||||
|
||||
function test_finalizes_when_quorum_is_reached() public {
|
||||
EveryChannelWitnessRegistry registry = new EveryChannelWitnessRegistry(address(this));
|
||||
WitnessActor witnessA = new WitnessActor();
|
||||
WitnessActor witnessB = new WitnessActor();
|
||||
registry.addWitness(address(witnessA));
|
||||
registry.addWitness(address(witnessB));
|
||||
|
||||
EveryChannelObservationLedger ledger = new EveryChannelObservationLedger(
|
||||
address(registry),
|
||||
2
|
||||
);
|
||||
|
||||
EveryChannelObservationLedger.ObservationHeader memory header = _header(
|
||||
keccak256("la-cbs:video0.m4s"),
|
||||
keccak256("epoch-229"),
|
||||
229,
|
||||
keccak256("58cd13f693debd000d995c9a5574e8b9274cc4d3399eb6f1f22393af1ba7407d")
|
||||
);
|
||||
|
||||
bytes32 observationHash = witnessA.propose(ledger, header);
|
||||
bytes32 slot = ledger.observationSlot(header.streamHash, header.epochHash);
|
||||
|
||||
assert(ledger.finalizedObservationBySlot(slot) == bytes32(0));
|
||||
|
||||
witnessB.attest(ledger, observationHash);
|
||||
|
||||
assert(ledger.finalizedObservationBySlot(slot) == observationHash);
|
||||
EveryChannelObservationLedger.ObservationHeader memory storedHeader = ledger
|
||||
.getObservationHeader(observationHash);
|
||||
assert(storedHeader.streamHash == header.streamHash);
|
||||
assert(storedHeader.epochHash == header.epochHash);
|
||||
assert(storedHeader.dataRoot == header.dataRoot);
|
||||
assert(storedHeader.sequence == header.sequence);
|
||||
}
|
||||
|
||||
function test_rejects_duplicate_attestation() public {
|
||||
EveryChannelWitnessRegistry registry = new EveryChannelWitnessRegistry(address(this));
|
||||
WitnessActor witnessA = new WitnessActor();
|
||||
registry.addWitness(address(witnessA));
|
||||
|
||||
EveryChannelObservationLedger ledger = new EveryChannelObservationLedger(
|
||||
address(registry),
|
||||
1
|
||||
);
|
||||
|
||||
EveryChannelObservationLedger.ObservationHeader memory header = _header(
|
||||
keccak256("la-nbc:catalog.json"),
|
||||
keccak256("epoch-5"),
|
||||
5,
|
||||
keccak256("f6ea0793fd00e29ced670d586e0e5f7f3d0f5edfc016c03f80710bd4bed587ec")
|
||||
);
|
||||
|
||||
bytes32 observationHash = witnessA.propose(ledger, header);
|
||||
assert(
|
||||
ledger.finalizedObservationBySlot(
|
||||
ledger.observationSlot(header.streamHash, header.epochHash)
|
||||
) == observationHash
|
||||
);
|
||||
|
||||
(bool ok, ) = address(witnessA).call(
|
||||
abi.encodeWithSelector(
|
||||
WitnessActor.attest.selector,
|
||||
ledger,
|
||||
observationHash
|
||||
)
|
||||
);
|
||||
assert(!ok);
|
||||
}
|
||||
|
||||
function test_competing_candidates_only_one_slot_can_finalize() public {
|
||||
EveryChannelWitnessRegistry registry = new EveryChannelWitnessRegistry(address(this));
|
||||
WitnessActor witnessA = new WitnessActor();
|
||||
WitnessActor witnessB = new WitnessActor();
|
||||
WitnessActor witnessC = new WitnessActor();
|
||||
registry.addWitness(address(witnessA));
|
||||
registry.addWitness(address(witnessB));
|
||||
registry.addWitness(address(witnessC));
|
||||
|
||||
EveryChannelObservationLedger ledger = new EveryChannelObservationLedger(
|
||||
address(registry),
|
||||
2
|
||||
);
|
||||
|
||||
bytes32 streamHash = keccak256("la-cbs:video0.m4s");
|
||||
bytes32 epochHash = keccak256("epoch-230");
|
||||
EveryChannelObservationLedger.ObservationHeader memory candidateA = _header(
|
||||
streamHash,
|
||||
epochHash,
|
||||
230,
|
||||
keccak256("1130c51b3ce428505dbc3c3294678f76e3200221d853fc9b02c8ed603ecc8b8c")
|
||||
);
|
||||
EveryChannelObservationLedger.ObservationHeader memory candidateB = _header(
|
||||
streamHash,
|
||||
epochHash,
|
||||
230,
|
||||
keccak256("deadbeef")
|
||||
);
|
||||
|
||||
bytes32 hashA = witnessA.propose(ledger, candidateA);
|
||||
bytes32 hashB = witnessB.propose(ledger, candidateB);
|
||||
bytes32 slot = ledger.observationSlot(streamHash, epochHash);
|
||||
|
||||
assert(hashA != hashB);
|
||||
assert(ledger.finalizedObservationBySlot(slot) == bytes32(0));
|
||||
|
||||
witnessC.attest(ledger, hashA);
|
||||
assert(ledger.finalizedObservationBySlot(slot) == hashA);
|
||||
|
||||
(bool ok, ) = address(witnessB).call(
|
||||
abi.encodeWithSelector(
|
||||
WitnessActor.attest.selector,
|
||||
ledger,
|
||||
hashB
|
||||
)
|
||||
);
|
||||
assert(!ok);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,3 +8,5 @@ license.workspace = true
|
|||
serde.workspace = true
|
||||
blake3.workspace = true
|
||||
serde_json.workspace = true
|
||||
hex = "0.4"
|
||||
sha3 = "0.10"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
//! Core types shared across every.channel.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha3::{Digest, Keccak256};
|
||||
use std::fmt;
|
||||
|
||||
pub const MANIFEST_ID_ALG_BLAKE3: &str = "blake3";
|
||||
pub const MANIFEST_ID_ALG_KECCAK256: &str = "keccak256";
|
||||
pub const MERKLE_PROOF_ALG_BLAKE3: &str = "merkle+blake3";
|
||||
pub const MERKLE_PROOF_ALG_KECCAK256: &str = "merkle+keccak256";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct ChannelId(pub String);
|
||||
|
||||
|
|
@ -12,6 +18,13 @@ pub struct DeviceId(pub String);
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct StreamId(pub String);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ChainCommitment {
|
||||
pub chain: String,
|
||||
pub scheme: String,
|
||||
pub digest: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StreamDescriptor {
|
||||
pub id: StreamId,
|
||||
|
|
@ -19,6 +32,8 @@ pub struct StreamDescriptor {
|
|||
pub number: Option<String>,
|
||||
pub source: String,
|
||||
pub metadata: Vec<StreamMetadata>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub commitments: Vec<ChainCommitment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -32,6 +47,7 @@ pub struct BroadcastId {
|
|||
pub standard: String,
|
||||
pub transport_stream_id: Option<u16>,
|
||||
pub program_number: Option<u16>,
|
||||
pub virtual_channel: Option<String>,
|
||||
pub callsign: Option<String>,
|
||||
pub region: Option<String>,
|
||||
pub frequency: Option<String>,
|
||||
|
|
@ -54,6 +70,30 @@ pub struct StreamKey {
|
|||
}
|
||||
|
||||
impl StreamKey {
|
||||
pub fn for_channel_or_source(
|
||||
channel: Option<&Channel>,
|
||||
standard: Option<&str>,
|
||||
source: SourceId,
|
||||
profile: Option<String>,
|
||||
variant: Option<String>,
|
||||
) -> Self {
|
||||
let broadcast = channel
|
||||
.and_then(|channel| standard.and_then(|standard| channel.broadcast_id(standard)));
|
||||
let source = if broadcast.is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(source)
|
||||
};
|
||||
|
||||
Self {
|
||||
version: 1,
|
||||
broadcast,
|
||||
source,
|
||||
profile,
|
||||
variant,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_stream_id(&self) -> StreamId {
|
||||
let mut parts = vec![
|
||||
"ec".to_string(),
|
||||
|
|
@ -70,6 +110,9 @@ impl StreamKey {
|
|||
if let Some(program) = broadcast.program_number {
|
||||
parts.push(format!("program-{program}"));
|
||||
}
|
||||
if let Some(channel) = &broadcast.virtual_channel {
|
||||
parts.push(format!("channel-{}", sanitize(channel)));
|
||||
}
|
||||
if let Some(callsign) = &broadcast.callsign {
|
||||
parts.push(format!("callsign-{}", sanitize(callsign)));
|
||||
}
|
||||
|
|
@ -132,6 +175,90 @@ pub enum ChannelMetadata {
|
|||
Extra(String, String),
|
||||
}
|
||||
|
||||
impl BroadcastId {
|
||||
pub fn is_usable(&self) -> bool {
|
||||
self.transport_stream_id.is_some()
|
||||
|| self.program_number.is_some()
|
||||
|| self
|
||||
.virtual_channel
|
||||
.as_ref()
|
||||
.is_some_and(|value| !value.trim().is_empty())
|
||||
|| self
|
||||
.callsign
|
||||
.as_ref()
|
||||
.is_some_and(|value| !value.trim().is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn broadcast_id(&self, standard: &str) -> Option<BroadcastId> {
|
||||
let standard = standard.trim().to_ascii_lowercase();
|
||||
if standard.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let callsign = channel_metadata_value(&self.metadata, "callsign").or_else(|| {
|
||||
let name = self.name.trim();
|
||||
(!name.is_empty()).then(|| name.to_string())
|
||||
});
|
||||
let region = channel_metadata_value(&self.metadata, "region");
|
||||
let frequency = channel_metadata_value(&self.metadata, "frequency");
|
||||
let transport_stream_id = channel_metadata_u16(&self.metadata, "transport_stream_id")
|
||||
.or_else(|| channel_metadata_u16(&self.metadata, "tsid"));
|
||||
let program_number = self
|
||||
.program_id
|
||||
.or_else(|| channel_metadata_u16(&self.metadata, "program_number"))
|
||||
.or_else(|| channel_metadata_u16(&self.metadata, "program_id"));
|
||||
let virtual_channel = self.number.as_ref().and_then(|value| {
|
||||
let trimmed = value.trim();
|
||||
(!trimmed.is_empty()).then(|| trimmed.to_string())
|
||||
});
|
||||
|
||||
let broadcast = BroadcastId {
|
||||
standard,
|
||||
transport_stream_id,
|
||||
program_number,
|
||||
virtual_channel,
|
||||
callsign,
|
||||
region,
|
||||
frequency,
|
||||
};
|
||||
|
||||
broadcast.is_usable().then_some(broadcast)
|
||||
}
|
||||
}
|
||||
|
||||
fn channel_metadata_value(metadata: &[ChannelMetadata], key: &str) -> Option<String> {
|
||||
for item in metadata {
|
||||
match item {
|
||||
ChannelMetadata::Callsign(value) if key == "callsign" => {
|
||||
return Some(value.trim().to_string())
|
||||
}
|
||||
ChannelMetadata::Region(value) if key == "region" => {
|
||||
return Some(value.trim().to_string())
|
||||
}
|
||||
ChannelMetadata::Frequency(value) if key == "frequency" => {
|
||||
return Some(value.trim().to_string())
|
||||
}
|
||||
ChannelMetadata::Network(value) if key == "network" => {
|
||||
return Some(value.trim().to_string())
|
||||
}
|
||||
ChannelMetadata::Extra(extra_key, value) if extra_key.eq_ignore_ascii_case(key) => {
|
||||
let trimmed = value.trim_matches('"').trim().to_string();
|
||||
if !trimmed.is_empty() {
|
||||
return Some(trimmed);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn channel_metadata_u16(metadata: &[ChannelMetadata], key: &str) -> Option<u16> {
|
||||
channel_metadata_value(metadata, key).and_then(|value| value.parse().ok())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PacketDigest {
|
||||
pub algorithm: String,
|
||||
|
|
@ -207,6 +334,8 @@ pub struct StreamControlAnnouncement {
|
|||
pub updated_unix_ms: u64,
|
||||
/// Suggested freshness window for this announcement.
|
||||
pub ttl_ms: u64,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub commitments: Vec<ChainCommitment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -218,6 +347,8 @@ pub struct ManifestSummary {
|
|||
pub chunk_start_index: u64,
|
||||
pub encoder_profile_id: String,
|
||||
pub signed_by: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub commitments: Vec<ChainCommitment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -268,6 +399,8 @@ pub struct Manifest {
|
|||
pub body: ManifestBody,
|
||||
pub manifest_id: String,
|
||||
pub signatures: Vec<ManifestSignature>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub commitments: Vec<ChainCommitment>,
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
|
|
@ -284,6 +417,7 @@ impl Manifest {
|
|||
.iter()
|
||||
.map(|sig| sig.signer_id.clone())
|
||||
.collect(),
|
||||
commitments: self.commitments.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -307,44 +441,96 @@ impl std::error::Error for ManifestError {}
|
|||
|
||||
impl ManifestBody {
|
||||
pub fn manifest_id(&self) -> Result<String, serde_json::Error> {
|
||||
self.manifest_id_blake3()
|
||||
}
|
||||
|
||||
pub fn manifest_id_blake3(&self) -> Result<String, serde_json::Error> {
|
||||
let bytes = serde_json::to_vec(self)?;
|
||||
Ok(blake3::hash(&bytes).to_hex().to_string())
|
||||
}
|
||||
|
||||
pub fn manifest_id_keccak256(&self) -> Result<String, serde_json::Error> {
|
||||
let bytes = serde_json::to_vec(self)?;
|
||||
Ok(hex::encode(keccak256(&bytes)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merkle_root_from_hashes(hashes: &[String]) -> Result<String, ManifestError> {
|
||||
fn parse_hash32(value: &str) -> Result<[u8; 32], ManifestError> {
|
||||
let trimmed = value.trim().strip_prefix("0x").unwrap_or(value.trim());
|
||||
let bytes = hex::decode(trimmed).map_err(|_| ManifestError::InvalidHash(value.to_string()))?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(ManifestError::InvalidHash(value.to_string()));
|
||||
}
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&bytes);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn keccak256(bytes: &[u8]) -> [u8; 32] {
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(bytes);
|
||||
let digest = hasher.finalize();
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&digest);
|
||||
out
|
||||
}
|
||||
|
||||
fn blake3_pair_hash(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
hasher.update(left);
|
||||
hasher.update(right);
|
||||
*hasher.finalize().as_bytes()
|
||||
}
|
||||
|
||||
fn keccak_pair_hash(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(left);
|
||||
merged[32..].copy_from_slice(right);
|
||||
keccak256(&merged)
|
||||
}
|
||||
|
||||
fn merkle_root_from_hashes_with(
|
||||
hashes: &[String],
|
||||
pair_hash: fn(&[u8; 32], &[u8; 32]) -> [u8; 32],
|
||||
) -> Result<String, ManifestError> {
|
||||
if hashes.is_empty() {
|
||||
return Err(ManifestError::Empty);
|
||||
}
|
||||
let mut nodes: Vec<blake3::Hash> = Vec::with_capacity(hashes.len());
|
||||
let mut nodes: Vec<[u8; 32]> = Vec::with_capacity(hashes.len());
|
||||
for hash in hashes {
|
||||
let parsed = blake3::Hash::from_hex(hash.as_bytes())
|
||||
.map_err(|_| ManifestError::InvalidHash(hash.clone()))?;
|
||||
nodes.push(parsed);
|
||||
nodes.push(parse_hash32(hash)?);
|
||||
}
|
||||
while nodes.len() > 1 {
|
||||
if nodes.len() % 2 == 1 {
|
||||
if let Some(last) = nodes.last().cloned() {
|
||||
if let Some(last) = nodes.last().copied() {
|
||||
nodes.push(last);
|
||||
}
|
||||
}
|
||||
let mut parents = Vec::with_capacity(nodes.len() / 2);
|
||||
for pair in nodes.chunks(2) {
|
||||
let left = pair[0].as_bytes();
|
||||
let right = pair[1].as_bytes();
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(left);
|
||||
merged[32..].copy_from_slice(right);
|
||||
parents.push(blake3::hash(&merged));
|
||||
parents.push(pair_hash(&pair[0], &pair[1]));
|
||||
}
|
||||
nodes = parents;
|
||||
}
|
||||
Ok(nodes[0].to_hex().to_string())
|
||||
Ok(hex::encode(nodes[0]))
|
||||
}
|
||||
|
||||
pub fn merkle_proof_for_index(
|
||||
pub fn merkle_root_from_hashes(hashes: &[String]) -> Result<String, ManifestError> {
|
||||
blake3_merkle_root_from_hashes(hashes)
|
||||
}
|
||||
|
||||
pub fn blake3_merkle_root_from_hashes(hashes: &[String]) -> Result<String, ManifestError> {
|
||||
merkle_root_from_hashes_with(hashes, blake3_pair_hash)
|
||||
}
|
||||
|
||||
pub fn keccak_merkle_root_from_hashes(hashes: &[String]) -> Result<String, ManifestError> {
|
||||
merkle_root_from_hashes_with(hashes, keccak_pair_hash)
|
||||
}
|
||||
|
||||
fn merkle_proof_for_index_with(
|
||||
hashes: &[String],
|
||||
index: usize,
|
||||
pair_hash: fn(&[u8; 32], &[u8; 32]) -> [u8; 32],
|
||||
) -> Result<Vec<String>, ManifestError> {
|
||||
if hashes.is_empty() {
|
||||
return Err(ManifestError::Empty);
|
||||
|
|
@ -355,18 +541,16 @@ pub fn merkle_proof_for_index(
|
|||
)));
|
||||
}
|
||||
|
||||
let mut nodes: Vec<blake3::Hash> = Vec::with_capacity(hashes.len());
|
||||
let mut nodes: Vec<[u8; 32]> = Vec::with_capacity(hashes.len());
|
||||
for hash in hashes {
|
||||
let parsed = blake3::Hash::from_hex(hash.as_bytes())
|
||||
.map_err(|_| ManifestError::InvalidHash(hash.clone()))?;
|
||||
nodes.push(parsed);
|
||||
nodes.push(parse_hash32(hash)?);
|
||||
}
|
||||
|
||||
let mut proof = Vec::new();
|
||||
let mut pos = index;
|
||||
while nodes.len() > 1 {
|
||||
if nodes.len() % 2 == 1 {
|
||||
if let Some(last) = nodes.last().cloned() {
|
||||
if let Some(last) = nodes.last().copied() {
|
||||
nodes.push(last);
|
||||
}
|
||||
}
|
||||
|
|
@ -375,16 +559,11 @@ pub fn merkle_proof_for_index(
|
|||
let sibling = nodes
|
||||
.get(sibling_index)
|
||||
.ok_or_else(|| ManifestError::InvalidHash("missing sibling".to_string()))?;
|
||||
proof.push(sibling.to_hex().to_string());
|
||||
proof.push(hex::encode(sibling));
|
||||
|
||||
let mut parents = Vec::with_capacity(nodes.len() / 2);
|
||||
for pair in nodes.chunks(2) {
|
||||
let left = pair[0].as_bytes();
|
||||
let right = pair[1].as_bytes();
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(left);
|
||||
merged[32..].copy_from_slice(right);
|
||||
parents.push(blake3::hash(&merged));
|
||||
parents.push(pair_hash(&pair[0], &pair[1]));
|
||||
}
|
||||
nodes = parents;
|
||||
pos /= 2;
|
||||
|
|
@ -393,17 +572,39 @@ pub fn merkle_proof_for_index(
|
|||
Ok(proof)
|
||||
}
|
||||
|
||||
pub fn verify_merkle_proof(
|
||||
pub fn merkle_proof_for_index(
|
||||
hashes: &[String],
|
||||
index: usize,
|
||||
) -> Result<Vec<String>, ManifestError> {
|
||||
blake3_merkle_proof_for_index(hashes, index)
|
||||
}
|
||||
|
||||
pub fn blake3_merkle_proof_for_index(
|
||||
hashes: &[String],
|
||||
index: usize,
|
||||
) -> Result<Vec<String>, ManifestError> {
|
||||
merkle_proof_for_index_with(hashes, index, blake3_pair_hash)
|
||||
}
|
||||
|
||||
pub fn keccak_merkle_proof_for_index(
|
||||
hashes: &[String],
|
||||
index: usize,
|
||||
) -> Result<Vec<String>, ManifestError> {
|
||||
merkle_proof_for_index_with(hashes, index, keccak_pair_hash)
|
||||
}
|
||||
|
||||
fn verify_merkle_proof_with(
|
||||
leaf_hash: &str,
|
||||
mut index: usize,
|
||||
branch: &[String],
|
||||
expected_root: &str,
|
||||
pair_hash: fn(&[u8; 32], &[u8; 32]) -> [u8; 32],
|
||||
) -> bool {
|
||||
let Ok(mut acc) = blake3::Hash::from_hex(leaf_hash.as_bytes()) else {
|
||||
let Ok(mut acc) = parse_hash32(leaf_hash) else {
|
||||
return false;
|
||||
};
|
||||
for sibling_hex in branch {
|
||||
let Ok(sibling) = blake3::Hash::from_hex(sibling_hex.as_bytes()) else {
|
||||
let Ok(sibling) = parse_hash32(sibling_hex) else {
|
||||
return false;
|
||||
};
|
||||
let (left, right) = if index % 2 == 0 {
|
||||
|
|
@ -411,13 +612,40 @@ pub fn verify_merkle_proof(
|
|||
} else {
|
||||
(sibling, acc)
|
||||
};
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(left.as_bytes());
|
||||
merged[32..].copy_from_slice(right.as_bytes());
|
||||
acc = blake3::hash(&merged);
|
||||
acc = pair_hash(&left, &right);
|
||||
index /= 2;
|
||||
}
|
||||
acc.to_hex().to_string() == expected_root
|
||||
match parse_hash32(expected_root) {
|
||||
Ok(root) => acc == root,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_merkle_proof(
|
||||
leaf_hash: &str,
|
||||
index: usize,
|
||||
branch: &[String],
|
||||
expected_root: &str,
|
||||
) -> bool {
|
||||
verify_blake3_merkle_proof(leaf_hash, index, branch, expected_root)
|
||||
}
|
||||
|
||||
pub fn verify_blake3_merkle_proof(
|
||||
leaf_hash: &str,
|
||||
index: usize,
|
||||
branch: &[String],
|
||||
expected_root: &str,
|
||||
) -> bool {
|
||||
verify_merkle_proof_with(leaf_hash, index, branch, expected_root, blake3_pair_hash)
|
||||
}
|
||||
|
||||
pub fn verify_keccak_merkle_proof(
|
||||
leaf_hash: &str,
|
||||
index: usize,
|
||||
branch: &[String],
|
||||
expected_root: &str,
|
||||
) -> bool {
|
||||
verify_merkle_proof_with(leaf_hash, index, branch, expected_root, keccak_pair_hash)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -446,11 +674,39 @@ mod tests {
|
|||
assert_ne!(id1, id2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_id_defaults_to_blake3() {
|
||||
let body = ManifestBody {
|
||||
stream_id: StreamId("s".to_string()),
|
||||
epoch_id: "e".to_string(),
|
||||
chunk_duration_ms: 2000,
|
||||
total_chunks: 1,
|
||||
chunk_start_index: 0,
|
||||
encoder_profile_id: "p".to_string(),
|
||||
merkle_root: "00".repeat(32),
|
||||
created_unix_ms: 1,
|
||||
metadata: Vec::new(),
|
||||
chunk_hashes: vec!["11".repeat(32)],
|
||||
variants: None,
|
||||
};
|
||||
let bytes = serde_json::to_vec(&body).unwrap();
|
||||
assert_eq!(
|
||||
body.manifest_id().unwrap(),
|
||||
blake3::hash(&bytes).to_hex().to_string()
|
||||
);
|
||||
assert_ne!(
|
||||
body.manifest_id().unwrap(),
|
||||
body.manifest_id_keccak256().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merkle_root_single_is_leaf() {
|
||||
let leaf = blake3::hash(b"leaf").to_hex().to_string();
|
||||
let root = merkle_root_from_hashes(&[leaf.clone()]).unwrap();
|
||||
assert_eq!(root, leaf);
|
||||
let keccak_root = keccak_merkle_root_from_hashes(&[leaf.clone()]).unwrap();
|
||||
assert_eq!(keccak_root, leaf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -476,6 +732,23 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keccak_merkle_proof_roundtrip_small_sets() {
|
||||
for size in 1..=9usize {
|
||||
let leaves = (0..size)
|
||||
.map(|i| blake3::hash(&[i as u8]).to_hex().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let root = keccak_merkle_root_from_hashes(&leaves).unwrap();
|
||||
for idx in 0..size {
|
||||
let proof = keccak_merkle_proof_for_index(&leaves, idx).unwrap();
|
||||
assert!(
|
||||
verify_keccak_merkle_proof(&leaves[idx], idx, &proof, &root),
|
||||
"size {size} idx {idx} failed"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merkle_proof_detects_tampering() {
|
||||
let leaves = (0..4usize)
|
||||
|
|
@ -486,4 +759,90 @@ mod tests {
|
|||
proof[0] = blake3::hash(b"evil").to_hex().to_string();
|
||||
assert!(!verify_merkle_proof(&leaves[2], 2, &proof, &root));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn channel_broadcast_id_uses_typed_and_extra_metadata() {
|
||||
let channel = Channel {
|
||||
id: ChannelId("kcbs".to_string()),
|
||||
name: "KCBS-HD".to_string(),
|
||||
number: Some("2.1".to_string()),
|
||||
program_id: Some(3),
|
||||
metadata: vec![
|
||||
ChannelMetadata::Callsign("KCBS".to_string()),
|
||||
ChannelMetadata::Region("los-angeles".to_string()),
|
||||
ChannelMetadata::Extra("tsid".to_string(), "42".to_string()),
|
||||
ChannelMetadata::Extra("frequency".to_string(), "573000000".to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
let broadcast = channel.broadcast_id("ATSC").unwrap();
|
||||
assert_eq!(broadcast.standard, "atsc");
|
||||
assert_eq!(broadcast.transport_stream_id, Some(42));
|
||||
assert_eq!(broadcast.program_number, Some(3));
|
||||
assert_eq!(broadcast.virtual_channel.as_deref(), Some("2.1"));
|
||||
assert_eq!(broadcast.callsign.as_deref(), Some("KCBS"));
|
||||
assert_eq!(broadcast.region.as_deref(), Some("los-angeles"));
|
||||
assert_eq!(broadcast.frequency.as_deref(), Some("573000000"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_key_prefers_broadcast_scope_when_channel_identity_exists() {
|
||||
let channel = Channel {
|
||||
id: ChannelId("kcbs".to_string()),
|
||||
name: "KCBS-HD".to_string(),
|
||||
number: Some("2.1".to_string()),
|
||||
program_id: None,
|
||||
metadata: vec![ChannelMetadata::Callsign("KCBS".to_string())],
|
||||
};
|
||||
let source = SourceId {
|
||||
kind: "hdhr".to_string(),
|
||||
device_id: Some("ABCDEF01".to_string()),
|
||||
channel: Some("2.1".to_string()),
|
||||
};
|
||||
|
||||
let key = StreamKey::for_channel_or_source(
|
||||
Some(&channel),
|
||||
Some("atsc"),
|
||||
source,
|
||||
Some("chunk-2000ms".to_string()),
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(key.broadcast.is_some());
|
||||
assert!(key.source.is_none());
|
||||
assert_eq!(
|
||||
key.to_stream_id().0,
|
||||
"ec/stream/v1/broadcast/atsc/channel-2_1/callsign-kcbs/profile-chunk-2000ms"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_key_falls_back_to_source_scope_without_channel_identity() {
|
||||
let channel = Channel {
|
||||
id: ChannelId("unknown".to_string()),
|
||||
name: "".to_string(),
|
||||
number: None,
|
||||
program_id: None,
|
||||
metadata: Vec::new(),
|
||||
};
|
||||
let source = SourceId {
|
||||
kind: "ts".to_string(),
|
||||
device_id: None,
|
||||
channel: Some("file.ts".to_string()),
|
||||
};
|
||||
|
||||
let key = StreamKey::for_channel_or_source(
|
||||
Some(&channel),
|
||||
Some("atsc"),
|
||||
source,
|
||||
Some("chunk-2000ms".to_string()),
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(key.broadcast.is_none());
|
||||
assert_eq!(
|
||||
key.to_stream_id().0,
|
||||
"ec/stream/v1/source/ts/channel-file_ts/profile-chunk-2000ms"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,8 @@ license.workspace = true
|
|||
blake3 = "1"
|
||||
chacha20poly1305 = "0.10"
|
||||
ed25519-dalek = { version = "2", features = ["pkcs8"] }
|
||||
ec-eth = { path = "../ec-eth" }
|
||||
hex = "0.4"
|
||||
ec-core = { path = "../ec-core" }
|
||||
k256 = { version = "0.13", features = ["ecdsa"] }
|
||||
sha3 = "0.10"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
//! Cryptographic helpers for every.channel.
|
||||
|
||||
use chacha20poly1305::{aead::Aead, KeyInit, XChaCha20Poly1305, XNonce};
|
||||
use ec_core::ManifestSignature;
|
||||
use ec_core::{ManifestBody, ManifestSignature};
|
||||
use ec_eth::{manifest_body_eip712_signing_hash, ETH_MANIFEST_SIG_ALG};
|
||||
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
|
||||
use k256::ecdsa::{
|
||||
RecoveryId as SecpRecoveryId, Signature as SecpSignature, SigningKey as SecpSigningKey,
|
||||
VerifyingKey as SecpVerifyingKey,
|
||||
};
|
||||
use sha3::{Digest, Keccak256};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
pub const MANIFEST_SIG_ALG: &str = "ed25519";
|
||||
pub const ETH_MANIFEST_SIGNING_KEY_ENV: &str = "EVERY_CHANNEL_ETH_MANIFEST_SIGNING_KEY";
|
||||
|
||||
pub const ENCRYPTION_ALG: &str = "xchacha20poly1305";
|
||||
|
||||
|
|
@ -83,19 +90,29 @@ pub struct ManifestKeypair {
|
|||
pub verifying_key: VerifyingKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EthereumManifestKeypair {
|
||||
pub signing_key: SecpSigningKey,
|
||||
pub verifying_key: SecpVerifyingKey,
|
||||
}
|
||||
|
||||
fn decode_env_hex_or_file(value: &str) -> Result<Vec<u8>, String> {
|
||||
let trimmed = value.trim();
|
||||
if std::path::Path::new(trimmed).exists() {
|
||||
let text = fs::read_to_string(trimmed).map_err(|err| err.to_string())?;
|
||||
hex::decode(text.trim().trim_start_matches("0x")).map_err(|err| err.to_string())
|
||||
} else {
|
||||
hex::decode(trimmed.trim_start_matches("0x")).map_err(|err| err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_manifest_keypair_from_env() -> Result<Option<ManifestKeypair>, String> {
|
||||
let value = match env::var("EVERY_CHANNEL_MANIFEST_SIGNING_KEY") {
|
||||
Ok(value) => value,
|
||||
Err(env::VarError::NotPresent) => return Ok(None),
|
||||
Err(err) => return Err(err.to_string()),
|
||||
};
|
||||
let trimmed = value.trim();
|
||||
let key_bytes = if std::path::Path::new(trimmed).exists() {
|
||||
let text = fs::read_to_string(trimmed).map_err(|err| err.to_string())?;
|
||||
hex::decode(text.trim()).map_err(|err| err.to_string())?
|
||||
} else {
|
||||
hex::decode(trimmed).map_err(|err| err.to_string())?
|
||||
};
|
||||
let key_bytes = decode_env_hex_or_file(&value)?;
|
||||
let bytes = if key_bytes.len() == 32 {
|
||||
key_bytes
|
||||
} else if key_bytes.len() == 64 {
|
||||
|
|
@ -113,10 +130,36 @@ pub fn load_manifest_keypair_from_env() -> Result<Option<ManifestKeypair>, Strin
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn load_ethereum_manifest_keypair_from_env() -> Result<Option<EthereumManifestKeypair>, String>
|
||||
{
|
||||
let value = match env::var(ETH_MANIFEST_SIGNING_KEY_ENV) {
|
||||
Ok(value) => value,
|
||||
Err(env::VarError::NotPresent) => return Ok(None),
|
||||
Err(err) => return Err(err.to_string()),
|
||||
};
|
||||
let key_bytes = decode_env_hex_or_file(&value)?;
|
||||
if key_bytes.len() != 32 {
|
||||
return Err("ethereum manifest signing key must be exactly 32 hex bytes".to_string());
|
||||
}
|
||||
let signing_key =
|
||||
SecpSigningKey::from_bytes((&key_bytes[..]).into()).map_err(|err| err.to_string())?;
|
||||
let verifying_key = *signing_key.verifying_key();
|
||||
Ok(Some(EthereumManifestKeypair {
|
||||
signing_key,
|
||||
verifying_key,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn signer_id_from_key(key: &VerifyingKey) -> String {
|
||||
format!("ed25519:{}", hex::encode(key.to_bytes()))
|
||||
}
|
||||
|
||||
pub fn ethereum_signer_id_from_key(key: &SecpVerifyingKey) -> String {
|
||||
let encoded = key.to_encoded_point(false);
|
||||
let digest = Keccak256::digest(&encoded.as_bytes()[1..]);
|
||||
format!("eth:0x{}", hex::encode(&digest[12..]))
|
||||
}
|
||||
|
||||
pub fn sign_manifest_id(manifest_id: &str, keypair: &ManifestKeypair) -> ManifestSignature {
|
||||
let signature: Signature = keypair.signing_key.sign(manifest_id.as_bytes());
|
||||
ManifestSignature {
|
||||
|
|
@ -126,6 +169,25 @@ pub fn sign_manifest_id(manifest_id: &str, keypair: &ManifestKeypair) -> Manifes
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sign_manifest_body_eip712(
|
||||
body: &ManifestBody,
|
||||
keypair: &EthereumManifestKeypair,
|
||||
) -> Result<ManifestSignature, String> {
|
||||
let digest = manifest_body_eip712_signing_hash(body).map_err(|err| err.to_string())?;
|
||||
let (signature, recovery_id) = keypair
|
||||
.signing_key
|
||||
.sign_prehash_recoverable(digest.as_slice())
|
||||
.map_err(|err| err.to_string())?;
|
||||
let mut bytes = Vec::with_capacity(65);
|
||||
bytes.extend_from_slice(&signature.to_bytes());
|
||||
bytes.push(recovery_id.to_byte());
|
||||
Ok(ManifestSignature {
|
||||
signer_id: ethereum_signer_id_from_key(&keypair.verifying_key),
|
||||
alg: ETH_MANIFEST_SIG_ALG.to_string(),
|
||||
signature: hex::encode(bytes),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_manifest_signature(manifest_id: &str, sig: &ManifestSignature) -> bool {
|
||||
if sig.alg != MANIFEST_SIG_ALG {
|
||||
return false;
|
||||
|
|
@ -156,6 +218,57 @@ pub fn verify_manifest_signature(manifest_id: &str, sig: &ManifestSignature) ->
|
|||
.is_ok()
|
||||
}
|
||||
|
||||
fn normalize_eth_signer_id(value: &str) -> Option<String> {
|
||||
let trimmed = value
|
||||
.strip_prefix("eth:")
|
||||
.or_else(|| value.strip_prefix("ETH:"))
|
||||
.unwrap_or(value)
|
||||
.trim();
|
||||
let address = trimmed.trim_start_matches("0x");
|
||||
if address.len() != 40 || !address.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
return None;
|
||||
}
|
||||
Some(format!("eth:0x{}", address.to_ascii_lowercase()))
|
||||
}
|
||||
|
||||
pub fn verify_manifest_body_eip712_signature(body: &ManifestBody, sig: &ManifestSignature) -> bool {
|
||||
if sig.alg != ETH_MANIFEST_SIG_ALG {
|
||||
return false;
|
||||
}
|
||||
let Some(expected_signer_id) = normalize_eth_signer_id(&sig.signer_id) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(sig_bytes) = hex::decode(sig.signature.trim().trim_start_matches("0x")) else {
|
||||
return false;
|
||||
};
|
||||
if sig_bytes.len() != 65 {
|
||||
return false;
|
||||
}
|
||||
let Ok(signature) = SecpSignature::from_slice(&sig_bytes[..64]) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(recovery_id) = SecpRecoveryId::try_from(sig_bytes[64]) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(digest) = manifest_body_eip712_signing_hash(body) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(verifying_key) =
|
||||
SecpVerifyingKey::recover_from_prehash(digest.as_slice(), &signature, recovery_id)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
ethereum_signer_id_from_key(&verifying_key) == expected_signer_id
|
||||
}
|
||||
|
||||
pub fn verify_manifest_signature_with_body(
|
||||
manifest_id: &str,
|
||||
body: &ManifestBody,
|
||||
sig: &ManifestSignature,
|
||||
) -> bool {
|
||||
verify_manifest_signature(manifest_id, sig) || verify_manifest_body_eip712_signature(body, sig)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -212,6 +325,35 @@ mod tests {
|
|||
assert!(!verify_manifest_signature("evil", &sig));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ethereum_manifest_sign_verify_roundtrip() {
|
||||
let body = ManifestBody {
|
||||
stream_id: ec_core::StreamId("stream".to_string()),
|
||||
epoch_id: "epoch-1".to_string(),
|
||||
chunk_duration_ms: 2000,
|
||||
total_chunks: 1,
|
||||
chunk_start_index: 0,
|
||||
encoder_profile_id: "p".to_string(),
|
||||
merkle_root: "11".repeat(32),
|
||||
created_unix_ms: 1,
|
||||
metadata: Vec::new(),
|
||||
chunk_hashes: vec!["22".repeat(32)],
|
||||
variants: None,
|
||||
};
|
||||
let secret = [7u8; 32];
|
||||
let signing_key = SecpSigningKey::from_bytes((&secret).into()).unwrap();
|
||||
let verifying_key = *signing_key.verifying_key();
|
||||
let keypair = EthereumManifestKeypair {
|
||||
signing_key,
|
||||
verifying_key,
|
||||
};
|
||||
let sig = sign_manifest_body_eip712(&body, &keypair).unwrap();
|
||||
assert!(verify_manifest_body_eip712_signature(&body, &sig));
|
||||
let mut tampered = body.clone();
|
||||
tampered.created_unix_ms = 2;
|
||||
assert!(!verify_manifest_body_eip712_signature(&tampered, &sig));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_keypair_from_env_hex() {
|
||||
let prev = env::var("EVERY_CHANNEL_MANIFEST_SIGNING_KEY").ok();
|
||||
|
|
@ -224,4 +366,17 @@ mod tests {
|
|||
None => env::remove_var("EVERY_CHANNEL_MANIFEST_SIGNING_KEY"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_ethereum_keypair_from_env_hex() {
|
||||
let prev = env::var(ETH_MANIFEST_SIGNING_KEY_ENV).ok();
|
||||
env::set_var(ETH_MANIFEST_SIGNING_KEY_ENV, "01".repeat(32));
|
||||
let loaded = load_ethereum_manifest_keypair_from_env().unwrap().unwrap();
|
||||
let id = ethereum_signer_id_from_key(&loaded.verifying_key);
|
||||
assert!(id.starts_with("eth:0x"));
|
||||
match prev {
|
||||
Some(value) => env::set_var(ETH_MANIFEST_SIGNING_KEY_ENV, value),
|
||||
None => env::remove_var(ETH_MANIFEST_SIGNING_KEY_ENV),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
crates/ec-eth/Cargo.toml
Normal file
12
crates/ec-eth/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "ec-eth"
|
||||
version = "0.0.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
alloy-primitives = "1.5.7"
|
||||
alloy-sol-types = "1.5.7"
|
||||
blake3 = "1"
|
||||
ec-core = { path = "../ec-core" }
|
||||
hex = "0.4"
|
||||
642
crates/ec-eth/src/lib.rs
Normal file
642
crates/ec-eth/src/lib.rs
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
//! Ethereum-compatible representations and commitments for every.channel core types.
|
||||
|
||||
use alloy_primitives::{keccak256, B256};
|
||||
use alloy_sol_types::{eip712_domain, sol, Eip712Domain, SolStruct, SolValue};
|
||||
use ec_core::{
|
||||
BroadcastId, ChainCommitment, ChunkId, Manifest, ManifestBody, ManifestSignature,
|
||||
ManifestVariant, SourceId, StreamControlAnnouncement, StreamDescriptor, StreamKey,
|
||||
StreamMetadata, StreamTransportDescriptor,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
pub const ETHEREUM_CHAIN: &str = "ethereum";
|
||||
pub const SCHEME_STREAM_ID_KECCAK: &str = "stream-id-keccak256-v1";
|
||||
pub const SCHEME_STREAM_DESCRIPTOR_ABI: &str = "stream-descriptor-abi-keccak256-v1";
|
||||
pub const SCHEME_CONTROL_ANNOUNCEMENT_ABI: &str = "control-announcement-abi-keccak256-v1";
|
||||
pub const SCHEME_MANIFEST_DATA_ROOT: &str = "manifest-data-merkle-keccak256-v1";
|
||||
pub const SCHEME_MANIFEST_BODY_ABI: &str = "manifest-body-abi-keccak256-v1";
|
||||
pub const SCHEME_MANIFEST_ENVELOPE_ABI: &str = "manifest-envelope-abi-keccak256-v1";
|
||||
pub const ETH_MANIFEST_SIG_ALG: &str = "secp256k1-eip712-manifest-body-v1";
|
||||
|
||||
sol! {
|
||||
struct EthStreamMetadata {
|
||||
string key;
|
||||
string value;
|
||||
}
|
||||
|
||||
struct EthBroadcastId {
|
||||
string standard;
|
||||
bool hasTransportStreamId;
|
||||
uint16 transportStreamId;
|
||||
bool hasProgramNumber;
|
||||
uint16 programNumber;
|
||||
string virtualChannel;
|
||||
string callsign;
|
||||
string region;
|
||||
string frequency;
|
||||
}
|
||||
|
||||
struct EthSourceId {
|
||||
string kind;
|
||||
string deviceId;
|
||||
string channel;
|
||||
}
|
||||
|
||||
struct EthStreamKey {
|
||||
uint16 version;
|
||||
bool hasBroadcast;
|
||||
EthBroadcastId broadcast;
|
||||
bool hasSource;
|
||||
EthSourceId source;
|
||||
string profile;
|
||||
string variant;
|
||||
}
|
||||
|
||||
struct EthStreamDescriptor {
|
||||
string id;
|
||||
string title;
|
||||
string number;
|
||||
string source;
|
||||
EthStreamMetadata[] metadata;
|
||||
}
|
||||
|
||||
struct EthStreamTransportDescriptor {
|
||||
uint8 kind;
|
||||
string url;
|
||||
string endpoint;
|
||||
string broadcastName;
|
||||
string trackName;
|
||||
}
|
||||
|
||||
struct EthStreamControlAnnouncement {
|
||||
EthStreamDescriptor stream;
|
||||
EthStreamTransportDescriptor[] transports;
|
||||
uint64 updatedUnixMs;
|
||||
uint64 ttlMs;
|
||||
}
|
||||
|
||||
struct EthChunkId {
|
||||
string streamId;
|
||||
string epochId;
|
||||
uint64 chunkIndex;
|
||||
bytes32 chunkHash;
|
||||
}
|
||||
|
||||
struct EthManifestVariant {
|
||||
string variantId;
|
||||
string streamId;
|
||||
uint64 chunkStartIndex;
|
||||
uint64 totalChunks;
|
||||
bytes32 merkleRoot;
|
||||
bytes32[] chunkHashes;
|
||||
EthStreamMetadata[] metadata;
|
||||
}
|
||||
|
||||
struct EthManifestBody {
|
||||
string streamId;
|
||||
string epochId;
|
||||
uint64 chunkDurationMs;
|
||||
uint64 totalChunks;
|
||||
uint64 chunkStartIndex;
|
||||
string encoderProfileId;
|
||||
bytes32 merkleRoot;
|
||||
uint64 createdUnixMs;
|
||||
EthStreamMetadata[] metadata;
|
||||
bytes32[] chunkHashes;
|
||||
EthManifestVariant[] variants;
|
||||
}
|
||||
|
||||
struct EthManifestSignature {
|
||||
string signerId;
|
||||
string alg;
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
struct EthManifest {
|
||||
EthManifestBody body;
|
||||
bytes32 manifestId;
|
||||
EthManifestSignature[] signatures;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EthCommitmentError {
|
||||
Empty,
|
||||
InvalidHex(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for EthCommitmentError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
EthCommitmentError::Empty => write!(f, "no hashes supplied"),
|
||||
EthCommitmentError::InvalidHex(value) => {
|
||||
write!(f, "invalid 32-byte hex value: {value}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EthCommitmentError {}
|
||||
|
||||
fn commitment(scheme: &str, digest: B256) -> ChainCommitment {
|
||||
ChainCommitment {
|
||||
chain: ETHEREUM_CHAIN.to_string(),
|
||||
scheme: scheme.to_string(),
|
||||
digest: format!("0x{}", hex::encode(digest)),
|
||||
}
|
||||
}
|
||||
|
||||
fn abi_commitment<T: SolValue>(scheme: &str, value: &T) -> ChainCommitment {
|
||||
commitment(scheme, keccak256(value.abi_encode()))
|
||||
}
|
||||
|
||||
fn parse_b256(value: &str) -> Result<B256, EthCommitmentError> {
|
||||
let trimmed = value.trim().strip_prefix("0x").unwrap_or(value.trim());
|
||||
let bytes =
|
||||
hex::decode(trimmed).map_err(|_| EthCommitmentError::InvalidHex(value.to_string()))?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(EthCommitmentError::InvalidHex(value.to_string()));
|
||||
}
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&bytes);
|
||||
Ok(B256::from(out))
|
||||
}
|
||||
|
||||
fn parse_bytes(value: &str) -> Vec<u8> {
|
||||
let trimmed = value.trim().strip_prefix("0x").unwrap_or(value.trim());
|
||||
hex::decode(trimmed).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn manifest_eip712_domain() -> Eip712Domain {
|
||||
eip712_domain! {
|
||||
name: "every.channel",
|
||||
version: "1",
|
||||
}
|
||||
}
|
||||
|
||||
fn eth_metadata(items: &[StreamMetadata]) -> Vec<EthStreamMetadata> {
|
||||
items
|
||||
.iter()
|
||||
.map(|item| EthStreamMetadata {
|
||||
key: item.key.clone(),
|
||||
value: item.value.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn eth_broadcast_id(value: &BroadcastId) -> EthBroadcastId {
|
||||
EthBroadcastId {
|
||||
standard: value.standard.clone(),
|
||||
hasTransportStreamId: value.transport_stream_id.is_some(),
|
||||
transportStreamId: value.transport_stream_id.unwrap_or_default(),
|
||||
hasProgramNumber: value.program_number.is_some(),
|
||||
programNumber: value.program_number.unwrap_or_default(),
|
||||
virtualChannel: value.virtual_channel.clone().unwrap_or_default(),
|
||||
callsign: value.callsign.clone().unwrap_or_default(),
|
||||
region: value.region.clone().unwrap_or_default(),
|
||||
frequency: value.frequency.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_source_id(value: &SourceId) -> EthSourceId {
|
||||
EthSourceId {
|
||||
kind: value.kind.clone(),
|
||||
deviceId: value.device_id.clone().unwrap_or_default(),
|
||||
channel: value.channel.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_stream_key(value: &StreamKey) -> EthStreamKey {
|
||||
EthStreamKey {
|
||||
version: value.version,
|
||||
hasBroadcast: value.broadcast.is_some(),
|
||||
broadcast: value
|
||||
.broadcast
|
||||
.as_ref()
|
||||
.map(eth_broadcast_id)
|
||||
.unwrap_or_else(|| EthBroadcastId {
|
||||
standard: String::new(),
|
||||
hasTransportStreamId: false,
|
||||
transportStreamId: 0,
|
||||
hasProgramNumber: false,
|
||||
programNumber: 0,
|
||||
virtualChannel: String::new(),
|
||||
callsign: String::new(),
|
||||
region: String::new(),
|
||||
frequency: String::new(),
|
||||
}),
|
||||
hasSource: value.source.is_some(),
|
||||
source: value
|
||||
.source
|
||||
.as_ref()
|
||||
.map(eth_source_id)
|
||||
.unwrap_or_else(|| EthSourceId {
|
||||
kind: String::new(),
|
||||
deviceId: String::new(),
|
||||
channel: String::new(),
|
||||
}),
|
||||
profile: value.profile.clone().unwrap_or_default(),
|
||||
variant: value.variant.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_stream_descriptor(value: &StreamDescriptor) -> EthStreamDescriptor {
|
||||
EthStreamDescriptor {
|
||||
id: value.id.0.clone(),
|
||||
title: value.title.clone(),
|
||||
number: value.number.clone().unwrap_or_default(),
|
||||
source: value.source.clone(),
|
||||
metadata: eth_metadata(&value.metadata),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_stream_transport_descriptor(
|
||||
value: &StreamTransportDescriptor,
|
||||
) -> EthStreamTransportDescriptor {
|
||||
match value {
|
||||
StreamTransportDescriptor::RelayMoq {
|
||||
url,
|
||||
broadcast_name,
|
||||
track_name,
|
||||
} => EthStreamTransportDescriptor {
|
||||
kind: 0,
|
||||
url: url.clone(),
|
||||
endpoint: String::new(),
|
||||
broadcastName: broadcast_name.clone(),
|
||||
trackName: track_name.clone(),
|
||||
},
|
||||
StreamTransportDescriptor::IrohDirect {
|
||||
endpoint,
|
||||
broadcast_name,
|
||||
track_name,
|
||||
} => EthStreamTransportDescriptor {
|
||||
kind: 1,
|
||||
url: String::new(),
|
||||
endpoint: endpoint.clone(),
|
||||
broadcastName: broadcast_name.clone(),
|
||||
trackName: track_name.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_stream_control_announcement(
|
||||
value: &StreamControlAnnouncement,
|
||||
) -> EthStreamControlAnnouncement {
|
||||
EthStreamControlAnnouncement {
|
||||
stream: eth_stream_descriptor(&value.stream),
|
||||
transports: value
|
||||
.transports
|
||||
.iter()
|
||||
.map(eth_stream_transport_descriptor)
|
||||
.collect(),
|
||||
updatedUnixMs: value.updated_unix_ms,
|
||||
ttlMs: value.ttl_ms,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_chunk_id(value: &ChunkId) -> Result<EthChunkId, EthCommitmentError> {
|
||||
Ok(EthChunkId {
|
||||
streamId: value.stream_id.0.clone(),
|
||||
epochId: value.epoch_id.clone(),
|
||||
chunkIndex: value.chunk_index,
|
||||
chunkHash: parse_b256(&value.chunk_hash)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eth_manifest_variant(
|
||||
value: &ManifestVariant,
|
||||
) -> Result<EthManifestVariant, EthCommitmentError> {
|
||||
Ok(EthManifestVariant {
|
||||
variantId: value.variant_id.clone(),
|
||||
streamId: value.stream_id.0.clone(),
|
||||
chunkStartIndex: value.chunk_start_index,
|
||||
totalChunks: value.total_chunks,
|
||||
merkleRoot: parse_b256(&value.merkle_root)?,
|
||||
chunkHashes: value
|
||||
.chunk_hashes
|
||||
.iter()
|
||||
.map(|hash| parse_b256(hash))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
metadata: eth_metadata(&value.metadata),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eth_manifest_body(value: &ManifestBody) -> Result<EthManifestBody, EthCommitmentError> {
|
||||
let variants = value
|
||||
.variants
|
||||
.as_ref()
|
||||
.map(|variants| {
|
||||
variants
|
||||
.iter()
|
||||
.map(eth_manifest_variant)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(EthManifestBody {
|
||||
streamId: value.stream_id.0.clone(),
|
||||
epochId: value.epoch_id.clone(),
|
||||
chunkDurationMs: value.chunk_duration_ms,
|
||||
totalChunks: value.total_chunks,
|
||||
chunkStartIndex: value.chunk_start_index,
|
||||
encoderProfileId: value.encoder_profile_id.clone(),
|
||||
merkleRoot: parse_b256(&value.merkle_root)?,
|
||||
createdUnixMs: value.created_unix_ms,
|
||||
metadata: eth_metadata(&value.metadata),
|
||||
chunkHashes: value
|
||||
.chunk_hashes
|
||||
.iter()
|
||||
.map(|hash| parse_b256(hash))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
variants,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn manifest_body_eip712_signing_hash(value: &ManifestBody) -> Result<B256, EthCommitmentError> {
|
||||
Ok(eth_manifest_body(value)?.eip712_signing_hash(&manifest_eip712_domain()))
|
||||
}
|
||||
|
||||
pub fn eth_manifest_signature(value: &ManifestSignature) -> EthManifestSignature {
|
||||
EthManifestSignature {
|
||||
signerId: value.signer_id.clone(),
|
||||
alg: value.alg.clone(),
|
||||
signature: parse_bytes(&value.signature).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eth_manifest(value: &Manifest) -> Result<EthManifest, EthCommitmentError> {
|
||||
Ok(EthManifest {
|
||||
body: eth_manifest_body(&value.body)?,
|
||||
manifestId: parse_b256(&value.manifest_id)?,
|
||||
signatures: value
|
||||
.signatures
|
||||
.iter()
|
||||
.map(eth_manifest_signature)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
fn keccak_merkle_root(leaves: &[B256]) -> Result<B256, EthCommitmentError> {
|
||||
if leaves.is_empty() {
|
||||
return Err(EthCommitmentError::Empty);
|
||||
}
|
||||
let mut nodes = leaves.to_vec();
|
||||
while nodes.len() > 1 {
|
||||
if nodes.len() % 2 == 1 {
|
||||
if let Some(last) = nodes.last().copied() {
|
||||
nodes.push(last);
|
||||
}
|
||||
}
|
||||
let mut parents = Vec::with_capacity(nodes.len() / 2);
|
||||
for pair in nodes.chunks(2) {
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(pair[0].as_slice());
|
||||
merged[32..].copy_from_slice(pair[1].as_slice());
|
||||
parents.push(keccak256(merged));
|
||||
}
|
||||
nodes = parents;
|
||||
}
|
||||
Ok(nodes[0])
|
||||
}
|
||||
|
||||
pub fn ethereum_merkle_root_from_hashes(hashes: &[String]) -> Result<String, EthCommitmentError> {
|
||||
let leaves = hashes
|
||||
.iter()
|
||||
.map(|hash| parse_b256(hash))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(format!("0x{}", hex::encode(keccak_merkle_root(&leaves)?)))
|
||||
}
|
||||
|
||||
pub fn ethereum_merkle_proof_for_index(
|
||||
hashes: &[String],
|
||||
index: usize,
|
||||
) -> Result<Vec<String>, EthCommitmentError> {
|
||||
if hashes.is_empty() {
|
||||
return Err(EthCommitmentError::Empty);
|
||||
}
|
||||
if index >= hashes.len() {
|
||||
return Err(EthCommitmentError::InvalidHex(format!(
|
||||
"index {index} out of bounds"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut nodes = hashes
|
||||
.iter()
|
||||
.map(|hash| parse_b256(hash))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut proof = Vec::new();
|
||||
let mut pos = index;
|
||||
while nodes.len() > 1 {
|
||||
if nodes.len() % 2 == 1 {
|
||||
if let Some(last) = nodes.last().copied() {
|
||||
nodes.push(last);
|
||||
}
|
||||
}
|
||||
let sibling_index = if pos % 2 == 0 { pos + 1 } else { pos - 1 };
|
||||
let sibling = nodes[sibling_index];
|
||||
proof.push(format!("0x{}", hex::encode(sibling)));
|
||||
|
||||
let mut parents = Vec::with_capacity(nodes.len() / 2);
|
||||
for pair in nodes.chunks(2) {
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(pair[0].as_slice());
|
||||
merged[32..].copy_from_slice(pair[1].as_slice());
|
||||
parents.push(keccak256(merged));
|
||||
}
|
||||
nodes = parents;
|
||||
pos /= 2;
|
||||
}
|
||||
|
||||
Ok(proof)
|
||||
}
|
||||
|
||||
pub fn verify_ethereum_merkle_proof(
|
||||
leaf_hash: &str,
|
||||
mut index: usize,
|
||||
branch: &[String],
|
||||
expected_root: &str,
|
||||
) -> bool {
|
||||
let Ok(mut acc) = parse_b256(leaf_hash) else {
|
||||
return false;
|
||||
};
|
||||
for sibling_hex in branch {
|
||||
let Ok(sibling) = parse_b256(sibling_hex) else {
|
||||
return false;
|
||||
};
|
||||
let (left, right) = if index % 2 == 0 {
|
||||
(acc, sibling)
|
||||
} else {
|
||||
(sibling, acc)
|
||||
};
|
||||
let mut merged = [0u8; 64];
|
||||
merged[..32].copy_from_slice(left.as_slice());
|
||||
merged[32..].copy_from_slice(right.as_slice());
|
||||
acc = keccak256(merged);
|
||||
index /= 2;
|
||||
}
|
||||
match parse_b256(expected_root) {
|
||||
Ok(root) => acc == root,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_id_commitment(stream_id: &str) -> ChainCommitment {
|
||||
commitment(SCHEME_STREAM_ID_KECCAK, keccak256(stream_id.as_bytes()))
|
||||
}
|
||||
|
||||
pub fn broadcast_id_commitment(value: &BroadcastId) -> ChainCommitment {
|
||||
abi_commitment("broadcast-id-abi-keccak256-v1", ð_broadcast_id(value))
|
||||
}
|
||||
|
||||
pub fn stream_key_commitment(value: &StreamKey) -> ChainCommitment {
|
||||
abi_commitment("stream-key-abi-keccak256-v1", ð_stream_key(value))
|
||||
}
|
||||
|
||||
pub fn stream_descriptor_commitments(value: &StreamDescriptor) -> Vec<ChainCommitment> {
|
||||
vec![
|
||||
stream_id_commitment(&value.id.0),
|
||||
abi_commitment(SCHEME_STREAM_DESCRIPTOR_ABI, ð_stream_descriptor(value)),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn control_announcement_commitments(value: &StreamControlAnnouncement) -> Vec<ChainCommitment> {
|
||||
vec![abi_commitment(
|
||||
SCHEME_CONTROL_ANNOUNCEMENT_ABI,
|
||||
ð_stream_control_announcement(value),
|
||||
)]
|
||||
}
|
||||
|
||||
fn manifest_data_root_commitment(
|
||||
body: &ManifestBody,
|
||||
) -> Result<ChainCommitment, EthCommitmentError> {
|
||||
let root = if let Some(variants) = body
|
||||
.variants
|
||||
.as_ref()
|
||||
.filter(|variants| !variants.is_empty())
|
||||
{
|
||||
let roots = variants
|
||||
.iter()
|
||||
.map(|variant| variant.merkle_root.clone())
|
||||
.collect::<Vec<_>>();
|
||||
ethereum_merkle_root_from_hashes(&roots)?
|
||||
} else {
|
||||
ethereum_merkle_root_from_hashes(&body.chunk_hashes)?
|
||||
};
|
||||
Ok(ChainCommitment {
|
||||
chain: ETHEREUM_CHAIN.to_string(),
|
||||
scheme: SCHEME_MANIFEST_DATA_ROOT.to_string(),
|
||||
digest: root,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn manifest_commitments(value: &Manifest) -> Result<Vec<ChainCommitment>, EthCommitmentError> {
|
||||
let eth_body = eth_manifest_body(&value.body)?;
|
||||
let eth_manifest = eth_manifest(value)?;
|
||||
Ok(vec![
|
||||
manifest_data_root_commitment(&value.body)?,
|
||||
abi_commitment(SCHEME_MANIFEST_BODY_ABI, ð_body),
|
||||
abi_commitment(SCHEME_MANIFEST_ENVELOPE_ABI, ð_manifest),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn manifest_commitments_match(value: &Manifest) -> Result<bool, EthCommitmentError> {
|
||||
let present = value
|
||||
.commitments
|
||||
.iter()
|
||||
.filter(|item| item.chain == ETHEREUM_CHAIN)
|
||||
.collect::<Vec<_>>();
|
||||
if present.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
let expected = manifest_commitments(value)?;
|
||||
Ok(present.into_iter().all(|actual| expected.contains(actual)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ec_core::{ManifestBody, StreamId};
|
||||
|
||||
fn sample_body() -> ManifestBody {
|
||||
let chunk_hashes = vec![
|
||||
blake3::hash(b"chunk0").to_hex().to_string(),
|
||||
blake3::hash(b"chunk1").to_hex().to_string(),
|
||||
];
|
||||
let merkle_root = ec_core::merkle_root_from_hashes(&chunk_hashes).unwrap();
|
||||
ManifestBody {
|
||||
stream_id: StreamId("ec/stream/v1/broadcast/atsc/tsid-42/program-3".to_string()),
|
||||
epoch_id: "epoch-1".to_string(),
|
||||
chunk_duration_ms: 2000,
|
||||
total_chunks: 2,
|
||||
chunk_start_index: 10,
|
||||
encoder_profile_id: "deterministic-h264-aac".to_string(),
|
||||
merkle_root,
|
||||
created_unix_ms: 1234,
|
||||
metadata: vec![StreamMetadata {
|
||||
key: "callsign".to_string(),
|
||||
value: "KCBS".to_string(),
|
||||
}],
|
||||
chunk_hashes,
|
||||
variants: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keccak_merkle_root_and_proof_roundtrip() {
|
||||
let body = sample_body();
|
||||
let root = ethereum_merkle_root_from_hashes(&body.chunk_hashes).unwrap();
|
||||
let proof = ethereum_merkle_proof_for_index(&body.chunk_hashes, 1).unwrap();
|
||||
assert!(verify_ethereum_merkle_proof(
|
||||
&body.chunk_hashes[1],
|
||||
1,
|
||||
&proof,
|
||||
&root
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_commitments_are_stable_and_match_present_values() {
|
||||
let body = sample_body();
|
||||
let manifest_id = body.manifest_id().unwrap();
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures: Vec::new(),
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
let commitments = manifest_commitments(&manifest).unwrap();
|
||||
assert_eq!(commitments.len(), 3);
|
||||
manifest.commitments = commitments.clone();
|
||||
assert!(manifest_commitments_match(&manifest).unwrap());
|
||||
manifest.commitments[0].digest = "0xdeadbeef".to_string();
|
||||
assert!(!manifest_commitments_match(&manifest).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_body_eip712_hash_is_stable() {
|
||||
let body = sample_body();
|
||||
let h1 = manifest_body_eip712_signing_hash(&body).unwrap();
|
||||
let h2 = manifest_body_eip712_signing_hash(&body).unwrap();
|
||||
assert_eq!(h1, h2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_descriptor_commitments_include_stream_id_and_descriptor_hashes() {
|
||||
let descriptor = StreamDescriptor {
|
||||
id: StreamId("ec/stream/v1/source/test/device-a/channel-b".to_string()),
|
||||
title: "Test".to_string(),
|
||||
number: Some("2.1".to_string()),
|
||||
source: "control".to_string(),
|
||||
metadata: vec![StreamMetadata {
|
||||
key: "broadcast".to_string(),
|
||||
value: "la-nbc".to_string(),
|
||||
}],
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
let commitments = stream_descriptor_commitments(&descriptor);
|
||||
assert_eq!(commitments.len(), 2);
|
||||
assert_eq!(commitments[0].scheme, SCHEME_STREAM_ID_KECCAK);
|
||||
assert_eq!(commitments[1].scheme, SCHEME_STREAM_DESCRIPTOR_ABI);
|
||||
}
|
||||
}
|
||||
|
|
@ -555,9 +555,39 @@ fn lineup_from_json_value(json: &Value, device_id: Option<&DeviceId>) -> Result<
|
|||
));
|
||||
}
|
||||
|
||||
if let Some(callsign) = json_string_field(entry, &["CallSign", "Callsign", "CallSignRaw"]) {
|
||||
metadata.push(ChannelMetadata::Callsign(callsign));
|
||||
}
|
||||
if let Some(network) = json_string_field(entry, &["Network"]) {
|
||||
metadata.push(ChannelMetadata::Network(network));
|
||||
}
|
||||
if let Some(region) = json_string_field(entry, &["Region", "Market"]) {
|
||||
metadata.push(ChannelMetadata::Region(region));
|
||||
}
|
||||
if let Some(frequency) = json_string_field(entry, &["Frequency", "FrequencyHz"]) {
|
||||
metadata.push(ChannelMetadata::Frequency(frequency));
|
||||
}
|
||||
|
||||
let program_id = json_u16_field(entry, &["ProgramNumber", "ProgramID", "Program"]);
|
||||
|
||||
if let Some(obj) = entry.as_object() {
|
||||
for (key, value) in obj.iter() {
|
||||
if key == "GuideNumber" || key == "GuideName" || key == "Tags" || key == "URL" {
|
||||
if key == "GuideNumber"
|
||||
|| key == "GuideName"
|
||||
|| key == "Tags"
|
||||
|| key == "URL"
|
||||
|| key == "CallSign"
|
||||
|| key == "Callsign"
|
||||
|| key == "CallSignRaw"
|
||||
|| key == "Network"
|
||||
|| key == "Region"
|
||||
|| key == "Market"
|
||||
|| key == "Frequency"
|
||||
|| key == "FrequencyHz"
|
||||
|| key == "ProgramNumber"
|
||||
|| key == "ProgramID"
|
||||
|| key == "Program"
|
||||
{
|
||||
continue;
|
||||
}
|
||||
metadata.push(ChannelMetadata::Extra(key.clone(), value.to_string()));
|
||||
|
|
@ -568,7 +598,7 @@ fn lineup_from_json_value(json: &Value, device_id: Option<&DeviceId>) -> Result<
|
|||
id,
|
||||
name: guide_name,
|
||||
number: parsed.guide_number,
|
||||
program_id: None,
|
||||
program_id,
|
||||
metadata,
|
||||
};
|
||||
|
||||
|
|
@ -583,6 +613,44 @@ fn lineup_from_json_value(json: &Value, device_id: Option<&DeviceId>) -> Result<
|
|||
Ok(output)
|
||||
}
|
||||
|
||||
fn json_string_field(value: &Value, keys: &[&str]) -> Option<String> {
|
||||
let obj = value.as_object()?;
|
||||
for key in keys {
|
||||
let Some(value) = obj.get(*key) else {
|
||||
continue;
|
||||
};
|
||||
let text = match value {
|
||||
Value::String(text) => text.trim().to_string(),
|
||||
Value::Number(number) => number.to_string(),
|
||||
_ => continue,
|
||||
};
|
||||
if !text.is_empty() {
|
||||
return Some(text);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn json_u16_field(value: &Value, keys: &[&str]) -> Option<u16> {
|
||||
let obj = value.as_object()?;
|
||||
for key in keys {
|
||||
let Some(value) = obj.get(*key) else {
|
||||
continue;
|
||||
};
|
||||
let parsed = match value {
|
||||
Value::Number(number) => number
|
||||
.as_u64()
|
||||
.and_then(|number| u16::try_from(number).ok()),
|
||||
Value::String(text) => text.trim().parse::<u16>().ok(),
|
||||
_ => None,
|
||||
};
|
||||
if parsed.is_some() {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -648,6 +716,9 @@ mod tests {
|
|||
"GuideName": "KCBS-HD",
|
||||
"Tags": "drm,encrypted,",
|
||||
"URL": "http://hdhr/auto/v2.1",
|
||||
"CallSign": "KCBS",
|
||||
"ProgramNumber": "3",
|
||||
"Frequency": "573000000",
|
||||
"Foo": "Bar"
|
||||
},
|
||||
{
|
||||
|
|
@ -662,8 +733,17 @@ mod tests {
|
|||
assert_eq!(entries[0].channel.id.0, "hdhr:ABCDEF01:2.1");
|
||||
assert_eq!(entries[0].channel.name, "KCBS-HD");
|
||||
assert_eq!(entries[0].channel.number.as_deref(), Some("2.1"));
|
||||
assert_eq!(entries[0].channel.program_id, Some(3));
|
||||
assert_eq!(entries[0].stream_url, "http://hdhr/auto/v2.1");
|
||||
assert!(entries[0].tags.iter().any(|t| t == "drm"));
|
||||
assert!(entries[0].channel.metadata.iter().any(|m| match m {
|
||||
ChannelMetadata::Callsign(value) => value == "KCBS",
|
||||
_ => false,
|
||||
}));
|
||||
assert!(entries[0].channel.metadata.iter().any(|m| match m {
|
||||
ChannelMetadata::Frequency(value) => value == "573000000",
|
||||
_ => false,
|
||||
}));
|
||||
assert!(entries[0].channel.metadata.iter().any(|m| match m {
|
||||
ChannelMetadata::Extra(key, value) => key == "guide_number" && value == "2.1",
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -730,6 +730,7 @@ mod tests {
|
|||
},
|
||||
manifest_id: "m".to_string(),
|
||||
signatures: Vec::new(),
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
let bytes = encode_manifest_frame(&manifest).unwrap();
|
||||
let decoded = decode_manifest_frame(&bytes).unwrap();
|
||||
|
|
@ -768,6 +769,7 @@ mod tests {
|
|||
body,
|
||||
manifest_id: manifest_id.clone(),
|
||||
signatures: vec![sig],
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
let bytes = encode_manifest_frame(&manifest).unwrap();
|
||||
let decoded = decode_manifest_frame(&bytes).unwrap();
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ ec-crypto = { path = "../ec-crypto" }
|
|||
ec-direct = { path = "../ec-direct" }
|
||||
ec-moq = { path = "../ec-moq" }
|
||||
ec-chopper = { path = "../ec-chopper" }
|
||||
ec-eth = { path = "../ec-eth" }
|
||||
ec-hdhomerun = { path = "../ec-hdhomerun" }
|
||||
ec-iroh = { path = "../ec-iroh" }
|
||||
ec-linux-iptv = { path = "../ec-linux-iptv" }
|
||||
ec-ts = { path = "../ec-ts" }
|
||||
hex = "0.4"
|
||||
iroh = "0.96"
|
||||
just-webrtc = "0.2"
|
||||
|
|
|
|||
|
|
@ -8,15 +8,21 @@ use clap::ValueEnum;
|
|||
use clap::{Parser, Subcommand};
|
||||
use ec_chopper::{build_manifest_body_for_chunks, TsChunk};
|
||||
use ec_core::{
|
||||
merkle_proof_for_index, verify_merkle_proof, Manifest, ManifestSummary, ManifestVariant,
|
||||
MoqStreamDescriptor, StreamCatalogEntry, StreamControlAnnouncement, StreamDescriptor,
|
||||
StreamEncryptionInfo, StreamId, StreamKey, StreamMetadata, StreamTransportDescriptor,
|
||||
merkle_proof_for_index, verify_merkle_proof, Manifest, ManifestBody, ManifestSummary,
|
||||
ManifestVariant, MoqStreamDescriptor, StreamCatalogEntry, StreamControlAnnouncement,
|
||||
StreamDescriptor, StreamEncryptionInfo, StreamId, StreamKey, StreamMetadata,
|
||||
StreamTransportDescriptor, MERKLE_PROOF_ALG_BLAKE3,
|
||||
};
|
||||
use ec_crypto::{
|
||||
decrypt_stream_data, encrypt_stream_data, load_manifest_keypair_from_env, sign_manifest_id,
|
||||
verify_manifest_signature, ENCRYPTION_ALG,
|
||||
decrypt_stream_data, encrypt_stream_data, load_ethereum_manifest_keypair_from_env,
|
||||
load_manifest_keypair_from_env, sign_manifest_body_eip712, sign_manifest_id,
|
||||
verify_manifest_signature_with_body, ENCRYPTION_ALG,
|
||||
};
|
||||
use ec_direct::{decode_direct_link, encode_direct_link, DirectCodeV1};
|
||||
use ec_eth::{
|
||||
control_announcement_commitments, manifest_commitments, manifest_commitments_match,
|
||||
stream_descriptor_commitments,
|
||||
};
|
||||
use ec_iroh::DiscoveryConfig;
|
||||
use ec_moq::{
|
||||
chunk_duration_secs, decode_object_frame, encode_object_frame, FileRelay, GroupId, HlsWriter,
|
||||
|
|
@ -779,7 +785,6 @@ fn ingest(args: IngestArgs) -> Result<()> {
|
|||
};
|
||||
|
||||
let source_id = source.source_id();
|
||||
let source_id_for_stream = source_id.clone();
|
||||
let reader = source.open_stream()?;
|
||||
let encoder_profile_id = if deterministic {
|
||||
"deterministic-h264-aac".to_string()
|
||||
|
|
@ -815,17 +820,12 @@ fn ingest(args: IngestArgs) -> Result<()> {
|
|||
let relay = FileRelay::new(args.relay_dir);
|
||||
let track = TrackName {
|
||||
namespace: "every.channel".to_string(),
|
||||
name: args.stream_id.unwrap_or_else(|| {
|
||||
StreamKey {
|
||||
version: 1,
|
||||
broadcast: None,
|
||||
source: Some(source_id_for_stream),
|
||||
profile: Some(format!("chunk-{}ms", args.chunk_ms)),
|
||||
variant: None,
|
||||
name: match args.stream_id {
|
||||
Some(stream_id) => stream_id,
|
||||
None => {
|
||||
default_stream_id_for_source(source.as_ref(), format!("chunk-{}ms", args.chunk_ms))?
|
||||
}
|
||||
.to_stream_id()
|
||||
.0
|
||||
}),
|
||||
},
|
||||
};
|
||||
let stream_id = StreamId(track.name.clone());
|
||||
let manifest_payload = build_manifest(
|
||||
|
|
@ -1072,12 +1072,52 @@ fn deterministic_enabled(flag: bool) -> bool {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn default_stream_id_for_source(
|
||||
source: &dyn StreamSource,
|
||||
profile: impl Into<String>,
|
||||
) -> Result<String> {
|
||||
let source_id = source.source_id();
|
||||
let profile = profile.into();
|
||||
let stream_key = match source.broadcast_id()? {
|
||||
Some(broadcast) => StreamKey {
|
||||
version: 1,
|
||||
broadcast: Some(broadcast),
|
||||
source: None,
|
||||
profile: Some(profile.clone()),
|
||||
variant: None,
|
||||
},
|
||||
None => StreamKey {
|
||||
version: 1,
|
||||
broadcast: None,
|
||||
source: Some(source_id),
|
||||
profile: Some(profile),
|
||||
variant: None,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(stream_key.to_stream_id().0)
|
||||
}
|
||||
|
||||
fn read_chunk_bytes_and_hash(path: &std::path::Path) -> Result<(Vec<u8>, String)> {
|
||||
let data = fs::read(path).with_context(|| format!("failed to read {}", path.display()))?;
|
||||
let hash = blake3::hash(&data).to_hex().to_string();
|
||||
Ok((data, hash))
|
||||
}
|
||||
|
||||
fn collect_manifest_signatures(
|
||||
manifest_id: &str,
|
||||
body: &ManifestBody,
|
||||
) -> Result<Vec<ec_core::ManifestSignature>> {
|
||||
let mut signatures = Vec::new();
|
||||
if let Some(keypair) = load_manifest_keypair_from_env().map_err(|err| anyhow!(err))? {
|
||||
signatures.push(sign_manifest_id(manifest_id, &keypair));
|
||||
}
|
||||
if let Some(keypair) = load_ethereum_manifest_keypair_from_env().map_err(|err| anyhow!(err))? {
|
||||
signatures.push(sign_manifest_body_eip712(body, &keypair).map_err(|err| anyhow!(err))?);
|
||||
}
|
||||
Ok(signatures)
|
||||
}
|
||||
|
||||
fn build_manifest(
|
||||
stream_id: StreamId,
|
||||
epoch_id: impl Into<String>,
|
||||
|
|
@ -1099,15 +1139,15 @@ fn build_manifest(
|
|||
&chunk_hashes,
|
||||
)?;
|
||||
let manifest_id = body.manifest_id()?;
|
||||
let mut signatures = Vec::new();
|
||||
if let Some(keypair) = load_manifest_keypair_from_env().map_err(|err| anyhow!(err))? {
|
||||
signatures.push(sign_manifest_id(&manifest_id, &keypair));
|
||||
}
|
||||
Ok(Manifest {
|
||||
let signatures = collect_manifest_signatures(&manifest_id, &body)?;
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures,
|
||||
})
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
manifest.commitments = manifest_commitments(&manifest).map_err(|err| anyhow!("{err}"))?;
|
||||
Ok(manifest)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -1224,15 +1264,15 @@ fn build_multi_variant_manifest(
|
|||
variants: Some(entries),
|
||||
};
|
||||
let manifest_id = body.manifest_id()?;
|
||||
let mut signatures = Vec::new();
|
||||
if let Some(keypair) = load_manifest_keypair_from_env().map_err(|err| anyhow!(err))? {
|
||||
signatures.push(sign_manifest_id(&manifest_id, &keypair));
|
||||
}
|
||||
Ok(Manifest {
|
||||
let signatures = collect_manifest_signatures(&manifest_id, &body)?;
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures,
|
||||
})
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
manifest.commitments = manifest_commitments(&manifest).map_err(|err| anyhow!("{err}"))?;
|
||||
Ok(manifest)
|
||||
}
|
||||
|
||||
struct EpochBuffer {
|
||||
|
|
@ -1343,10 +1383,13 @@ fn validate_manifest(manifest: &Manifest, allowlist: Option<&HashSet<String>>) -
|
|||
_ => return false,
|
||||
}
|
||||
}
|
||||
if !matches!(manifest_commitments_match(manifest), Ok(true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(allowlist) = allowlist {
|
||||
return manifest.signatures.iter().any(|sig| {
|
||||
verify_manifest_signature(&manifest.manifest_id, sig)
|
||||
verify_manifest_signature_with_body(&manifest.manifest_id, &manifest.body, sig)
|
||||
&& allowlist.contains(&sig.signer_id)
|
||||
});
|
||||
}
|
||||
|
|
@ -1363,7 +1406,7 @@ fn validate_manifest(manifest: &Manifest, allowlist: Option<&HashSet<String>>) -
|
|||
manifest
|
||||
.signatures
|
||||
.iter()
|
||||
.any(|sig| verify_manifest_signature(&manifest.manifest_id, sig))
|
||||
.any(|sig| verify_manifest_signature_with_body(&manifest.manifest_id, &manifest.body, sig))
|
||||
}
|
||||
|
||||
fn strip_init_suffix(key_id: &str) -> &str {
|
||||
|
|
@ -1482,7 +1525,7 @@ fn build_object(
|
|||
chunk_hash: Some(chunk_hash),
|
||||
chunk_hash_alg: Some("blake3".to_string()),
|
||||
chunk_proof,
|
||||
chunk_proof_alg: Some("merkle+blake3".to_string()),
|
||||
chunk_proof_alg: Some(MERKLE_PROOF_ALG_BLAKE3.to_string()),
|
||||
manifest_id: manifest_id.map(|value| value.to_string()),
|
||||
};
|
||||
|
||||
|
|
@ -1592,6 +1635,8 @@ fn flush_epoch_publish(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ec_core::BroadcastId;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn parse_manifest_allowlist_splits_and_trims() {
|
||||
|
|
@ -1653,11 +1698,14 @@ mod tests {
|
|||
vec![sig]
|
||||
};
|
||||
|
||||
Manifest {
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures,
|
||||
}
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
manifest.commitments = manifest_commitments(&manifest).unwrap();
|
||||
manifest
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1684,6 +1732,65 @@ mod tests {
|
|||
assert!(!validate_manifest(&manifest, Some(&deny)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_manifest_accepts_ethereum_signature() {
|
||||
let chunk_hashes = vec![blake3::hash(b"c0").to_hex().to_string()];
|
||||
let body = build_manifest_body_for_chunks(
|
||||
StreamId("s".to_string()),
|
||||
"epoch-eth",
|
||||
2000,
|
||||
0,
|
||||
"p",
|
||||
1,
|
||||
Vec::new(),
|
||||
&chunk_hashes,
|
||||
)
|
||||
.unwrap();
|
||||
let manifest_id = body.manifest_id().unwrap();
|
||||
let prev = std::env::var(ec_crypto::ETH_MANIFEST_SIGNING_KEY_ENV).ok();
|
||||
std::env::set_var(ec_crypto::ETH_MANIFEST_SIGNING_KEY_ENV, "22".repeat(32));
|
||||
let keypair = load_ethereum_manifest_keypair_from_env().unwrap().unwrap();
|
||||
let sig = sign_manifest_body_eip712(&body, &keypair).unwrap();
|
||||
match prev {
|
||||
Some(value) => std::env::set_var(ec_crypto::ETH_MANIFEST_SIGNING_KEY_ENV, value),
|
||||
None => std::env::remove_var(ec_crypto::ETH_MANIFEST_SIGNING_KEY_ENV),
|
||||
}
|
||||
|
||||
let mut manifest = Manifest {
|
||||
body,
|
||||
manifest_id,
|
||||
signatures: vec![sig],
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
manifest.commitments = manifest_commitments(&manifest).unwrap();
|
||||
assert!(validate_manifest(&manifest, None));
|
||||
let allow = HashSet::from([manifest.signatures[0].signer_id.clone()]);
|
||||
assert!(validate_manifest(&manifest, Some(&allow)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_manifest_rejects_bad_ethereum_commitment() {
|
||||
let mut manifest = build_valid_manifest(true);
|
||||
manifest.commitments[0].digest = "0xdeadbeef".to_string();
|
||||
assert!(!validate_manifest(&manifest, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn control_announcement_carries_stream_and_announcement_commitments() {
|
||||
let announcement = build_control_announcement(
|
||||
"ec/stream/v1/broadcast/atsc/tsid-42/program-3".to_string(),
|
||||
"KCBS".to_string(),
|
||||
vec![StreamTransportDescriptor::IrohDirect {
|
||||
endpoint: "ed25519:node".to_string(),
|
||||
broadcast_name: "kcbs".to_string(),
|
||||
track_name: "video0.m4s".to_string(),
|
||||
}],
|
||||
5_000,
|
||||
);
|
||||
assert!(!announcement.stream.commitments.is_empty());
|
||||
assert!(!announcement.commitments.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_hash_for_chunk_indexes_into_hash_list() {
|
||||
let manifest = build_valid_manifest(true);
|
||||
|
|
@ -1699,6 +1806,70 @@ mod tests {
|
|||
);
|
||||
assert!(manifest_hash_for_chunk(&manifest, sid, 12).is_none());
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummySource {
|
||||
source_id: ec_core::SourceId,
|
||||
broadcast_id: Option<BroadcastId>,
|
||||
}
|
||||
|
||||
impl StreamSource for DummySource {
|
||||
fn open_stream(&self) -> Result<Box<dyn Read + Send>> {
|
||||
Ok(Box::new(Cursor::new(Vec::<u8>::new())))
|
||||
}
|
||||
|
||||
fn source_id(&self) -> ec_core::SourceId {
|
||||
self.source_id.clone()
|
||||
}
|
||||
|
||||
fn broadcast_id(&self) -> Result<Option<BroadcastId>> {
|
||||
Ok(self.broadcast_id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_stream_id_for_source_uses_broadcast_identity_when_present() {
|
||||
let source = DummySource {
|
||||
source_id: ec_core::SourceId {
|
||||
kind: "hdhr".to_string(),
|
||||
device_id: Some("ABCDEF01".to_string()),
|
||||
channel: Some("2.1".to_string()),
|
||||
},
|
||||
broadcast_id: Some(BroadcastId {
|
||||
standard: "atsc".to_string(),
|
||||
transport_stream_id: None,
|
||||
program_number: Some(3),
|
||||
virtual_channel: Some("2.1".to_string()),
|
||||
callsign: Some("KCBS".to_string()),
|
||||
region: None,
|
||||
frequency: None,
|
||||
}),
|
||||
};
|
||||
|
||||
let stream_id = default_stream_id_for_source(&source, "chunk-2000ms").unwrap();
|
||||
assert_eq!(
|
||||
stream_id,
|
||||
"ec/stream/v1/broadcast/atsc/program-3/channel-2_1/callsign-kcbs/profile-chunk-2000ms"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_stream_id_for_source_falls_back_to_source_identity() {
|
||||
let source = DummySource {
|
||||
source_id: ec_core::SourceId {
|
||||
kind: "ts".to_string(),
|
||||
device_id: None,
|
||||
channel: Some("capture.ts".to_string()),
|
||||
},
|
||||
broadcast_id: None,
|
||||
};
|
||||
|
||||
let stream_id = default_stream_id_for_source(&source, "chunk-2000ms").unwrap();
|
||||
assert_eq!(
|
||||
stream_id,
|
||||
"ec/stream/v1/source/ts/channel-capture_ts/profile-chunk-2000ms"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn moq_publish(args: MoqPublishArgs) -> Result<()> {
|
||||
|
|
@ -1747,19 +1918,13 @@ async fn moq_publish(args: MoqPublishArgs) -> Result<()> {
|
|||
};
|
||||
|
||||
let source_id = source.source_id();
|
||||
let source_id_for_stream = source_id.clone();
|
||||
|
||||
let stream_id = args.stream_id.unwrap_or_else(|| {
|
||||
StreamKey {
|
||||
version: 1,
|
||||
broadcast: None,
|
||||
source: Some(source_id_for_stream),
|
||||
profile: Some(format!("chunk-{}ms", args.chunk_ms)),
|
||||
variant: None,
|
||||
let stream_id = match args.stream_id {
|
||||
Some(stream_id) => stream_id,
|
||||
None => {
|
||||
default_stream_id_for_source(source.as_ref(), format!("chunk-{}ms", args.chunk_ms))?
|
||||
}
|
||||
.to_stream_id()
|
||||
.0
|
||||
});
|
||||
};
|
||||
|
||||
let broadcast_name = args.broadcast_name.unwrap_or_else(|| stream_id.clone());
|
||||
let track_name = args.track_name.clone();
|
||||
|
|
@ -4409,7 +4574,10 @@ fn build_catalog_entry(
|
|||
key: "broadcast".to_string(),
|
||||
value: broadcast_name.to_string(),
|
||||
}],
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
let mut stream = stream;
|
||||
stream.commitments = stream_descriptor_commitments(&stream);
|
||||
|
||||
let encryption = StreamEncryptionInfo {
|
||||
alg: ENCRYPTION_ALG.to_string(),
|
||||
|
|
@ -4448,7 +4616,7 @@ fn build_control_announcement(
|
|||
transports: Vec<StreamTransportDescriptor>,
|
||||
ttl_ms: u64,
|
||||
) -> StreamControlAnnouncement {
|
||||
let stream = StreamDescriptor {
|
||||
let mut stream = StreamDescriptor {
|
||||
id: StreamId(stream_id.clone()),
|
||||
title,
|
||||
number: None,
|
||||
|
|
@ -4457,14 +4625,19 @@ fn build_control_announcement(
|
|||
key: "stream_id".to_string(),
|
||||
value: stream_id,
|
||||
}],
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
stream.commitments = stream_descriptor_commitments(&stream);
|
||||
|
||||
StreamControlAnnouncement {
|
||||
let mut announcement = StreamControlAnnouncement {
|
||||
stream,
|
||||
transports,
|
||||
updated_unix_ms: now_unix_ms(),
|
||||
ttl_ms,
|
||||
}
|
||||
commitments: Vec::new(),
|
||||
};
|
||||
announcement.commitments = control_announcement_commitments(&announcement);
|
||||
announcement
|
||||
}
|
||||
|
||||
async fn spawn_control_announcer_task(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use clap::ValueEnum;
|
||||
use ec_chopper::{deterministic_h264_profile, ffmpeg_profile_args};
|
||||
use ec_core::SourceId;
|
||||
use ec_hdhomerun::{find_lineup_entry_by_name, find_lineup_entry_by_number};
|
||||
use ec_core::{BroadcastId, SourceId};
|
||||
use ec_hdhomerun::{find_lineup_entry_by_name, find_lineup_entry_by_number, LineupEntry};
|
||||
use ec_linux_iptv::LinuxDvbConfig;
|
||||
use ec_ts::probe_transport_stream_identity;
|
||||
use std::io::Read;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::thread;
|
||||
|
|
@ -11,6 +12,9 @@ use std::thread;
|
|||
pub trait StreamSource: Send {
|
||||
fn open_stream(&self) -> Result<Box<dyn Read + Send>>;
|
||||
fn source_id(&self) -> SourceId;
|
||||
fn broadcast_id(&self) -> Result<Option<BroadcastId>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -24,20 +28,8 @@ pub struct HdhrSource {
|
|||
|
||||
impl StreamSource for HdhrSource {
|
||||
fn open_stream(&self) -> Result<Box<dyn Read + Send>> {
|
||||
let device = resolve_hdhr_device(self)?;
|
||||
let lineup = ec_hdhomerun::fetch_lineup(&device)?;
|
||||
let entry = if let Some(channel) = &self.channel {
|
||||
find_lineup_entry_by_number(&lineup, channel)
|
||||
.or_else(|| find_lineup_entry_by_name(&lineup, channel))
|
||||
.ok_or_else(|| anyhow!("channel not found: {channel}"))?
|
||||
} else if let Some(name) = &self.name {
|
||||
find_lineup_entry_by_name(&lineup, name)
|
||||
.ok_or_else(|| anyhow!("channel not found: {name}"))?
|
||||
} else {
|
||||
return Err(anyhow!("--channel or --name required for hdhr"));
|
||||
};
|
||||
|
||||
Ok(Box::new(ec_hdhomerun::open_stream_entry(entry, None)?))
|
||||
let entry = resolve_hdhr_lineup_entry(self)?;
|
||||
Ok(Box::new(ec_hdhomerun::open_stream_entry(&entry, None)?))
|
||||
}
|
||||
|
||||
fn source_id(&self) -> SourceId {
|
||||
|
|
@ -48,6 +40,21 @@ impl StreamSource for HdhrSource {
|
|||
channel: self.channel.clone().or_else(|| self.name.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_id(&self) -> Result<Option<BroadcastId>> {
|
||||
let entry = resolve_hdhr_lineup_entry(self)?;
|
||||
let mut broadcast = entry.channel.broadcast_id("atsc");
|
||||
if broadcast.as_ref().is_none_or(|identity| {
|
||||
identity.transport_stream_id.is_none() || identity.program_number.is_none()
|
||||
}) {
|
||||
let probe = ec_hdhomerun::open_stream_entry(&entry, Some(2))?;
|
||||
broadcast = merge_broadcast_identity(
|
||||
broadcast,
|
||||
probe_transport_stream_broadcast(Box::new(probe), Some("atsc"))?,
|
||||
);
|
||||
}
|
||||
Ok(broadcast)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_hdhr_device(source: &HdhrSource) -> Result<ec_hdhomerun::HdhomerunDevice> {
|
||||
|
|
@ -72,6 +79,23 @@ fn resolve_hdhr_device(source: &HdhrSource) -> Result<ec_hdhomerun::HdhomerunDev
|
|||
.ok_or_else(|| anyhow!("no HDHomeRun devices found"))
|
||||
}
|
||||
|
||||
fn resolve_hdhr_lineup_entry(source: &HdhrSource) -> Result<LineupEntry> {
|
||||
let device = resolve_hdhr_device(source)?;
|
||||
let lineup = ec_hdhomerun::fetch_lineup(&device)?;
|
||||
let entry = if let Some(channel) = &source.channel {
|
||||
find_lineup_entry_by_number(&lineup, channel)
|
||||
.or_else(|| find_lineup_entry_by_name(&lineup, channel))
|
||||
.ok_or_else(|| anyhow!("channel not found: {channel}"))?
|
||||
} else if let Some(name) = &source.name {
|
||||
find_lineup_entry_by_name(&lineup, name)
|
||||
.ok_or_else(|| anyhow!("channel not found: {name}"))?
|
||||
} else {
|
||||
return Err(anyhow!("--channel or --name required for hdhr"));
|
||||
};
|
||||
|
||||
Ok(entry.clone())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LinuxDvbSource {
|
||||
pub adapter: u32,
|
||||
|
|
@ -126,6 +150,16 @@ impl StreamSource for TsSource {
|
|||
channel: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_id(&self) -> Result<Option<BroadcastId>> {
|
||||
if self.input.starts_with("http://") || self.input.starts_with("https://") {
|
||||
let reader = ec_hdhomerun::open_stream_url(&self.input, Some(2))?;
|
||||
probe_transport_stream_broadcast(Box::new(reader), None)
|
||||
} else {
|
||||
let reader = std::fs::File::open(&self.input)?;
|
||||
probe_transport_stream_broadcast(Box::new(reader), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
|
|
@ -197,6 +231,67 @@ impl StreamSource for HlsSource {
|
|||
}
|
||||
}
|
||||
|
||||
fn probe_transport_stream_broadcast(
|
||||
reader: Box<dyn Read + Send>,
|
||||
fallback_standard: Option<&str>,
|
||||
) -> Result<Option<BroadcastId>> {
|
||||
let Some(identity) = probe_transport_stream_identity(reader, 256)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let virtual_channel = match (identity.major_channel_number, identity.minor_channel_number) {
|
||||
(Some(major), Some(minor)) => Some(format!("{major}.{minor}")),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(Some(BroadcastId {
|
||||
standard: identity
|
||||
.standard
|
||||
.or_else(|| fallback_standard.map(|value| value.to_string()))
|
||||
.unwrap_or_else(|| "mpegts".to_string()),
|
||||
transport_stream_id: Some(identity.transport_stream_id),
|
||||
program_number: identity.program_number,
|
||||
virtual_channel,
|
||||
callsign: identity.short_name,
|
||||
region: None,
|
||||
frequency: None,
|
||||
}))
|
||||
}
|
||||
|
||||
fn merge_broadcast_identity(
|
||||
base: Option<BroadcastId>,
|
||||
probed: Option<BroadcastId>,
|
||||
) -> Option<BroadcastId> {
|
||||
match (base, probed) {
|
||||
(None, other) => other,
|
||||
(some, None) => some,
|
||||
(Some(mut base), Some(probed)) => {
|
||||
if base.standard.trim().is_empty() {
|
||||
base.standard = probed.standard;
|
||||
}
|
||||
if base.transport_stream_id.is_none() {
|
||||
base.transport_stream_id = probed.transport_stream_id;
|
||||
}
|
||||
if base.program_number.is_none() {
|
||||
base.program_number = probed.program_number;
|
||||
}
|
||||
if base.virtual_channel.is_none() {
|
||||
base.virtual_channel = probed.virtual_channel;
|
||||
}
|
||||
if base.callsign.is_none() {
|
||||
base.callsign = probed.callsign;
|
||||
}
|
||||
if base.region.is_none() {
|
||||
base.region = probed.region;
|
||||
}
|
||||
if base.frequency.is_none() {
|
||||
base.frequency = probed.frequency;
|
||||
}
|
||||
Some(base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FfmpegChildStream {
|
||||
child: Child,
|
||||
stdout: std::process::ChildStdout,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -24,13 +24,24 @@
|
|||
- Each chunk becomes a MoQ object in a group.
|
||||
- Objects are named and addressed deterministically.
|
||||
|
||||
5. Relay mesh
|
||||
5. Settlement rails
|
||||
|
||||
- Ethereum-compatible commitments mirror stream identity, manifests, and transport announcements.
|
||||
- Observation consensus is a separate rail on top of those commitments: a reality-derived
|
||||
`ObservationHeader` is hashed, witnessed, and finalized per `(stream, epoch)` slot.
|
||||
- The chain stores compact commitment pointers only; media bytes and full manifests remain on iroh,
|
||||
relays, and archive storage.
|
||||
- OP Stack is the current private-chain operator target, with `ecp-forge` as the head node for the
|
||||
first Sepolia-anchored testnet tranche.
|
||||
- Private-chain operation is a protocol extension, not a replacement for transport.
|
||||
|
||||
6. Relay mesh
|
||||
|
||||
- Relays cache objects and announce tracks.
|
||||
- iroh provides programmable topology and peer routing.
|
||||
- Multiple relays can serve identical objects.
|
||||
|
||||
6. Playback
|
||||
7. Playback
|
||||
|
||||
- Desktop: Tauri app that subscribes to tracks.
|
||||
- CLI: debugging, inspection, and headless clients.
|
||||
|
|
@ -43,15 +54,30 @@
|
|||
- Relay: stores and forwards MoQ objects.
|
||||
- Manager: configures nodes and applies policy.
|
||||
- Provisioner: bootstraps nodes and manages deployment.
|
||||
- Witness: attests to a reality-derived observation hash for a stream epoch.
|
||||
|
||||
## Determinism
|
||||
|
||||
- The same input with the same profile should yield identical chunks.
|
||||
- Chunk hashes are the primitive for availability and de-duplication.
|
||||
- Deterministic names allow relays to converge without coordination.
|
||||
- Observation consensus derives a second deterministic summary from the archive/manifests layer:
|
||||
`streamHash`, `epochHash`, `dataRoot`, and `locatorHash` become the on-chain observation header.
|
||||
- Local manifests keep BLAKE3 `manifest_id`s and `merkle+blake3` proofs, while the Ethereum rail
|
||||
adds Keccak ABI/data commitments and optional secp256k1 EIP-712 body signatures for settlement.
|
||||
- Discovery identity should prefer broadcast-scoped channel identity when available and only fall
|
||||
back to source-scoped IDs when the ingest path cannot yet prove a usable broadcast identity.
|
||||
- PAT-derived identity is accepted only when the stream exposes a single non-zero program; ambiguous
|
||||
multi-program TS inputs remain source-scoped to avoid accidental convergence on the wrong channel.
|
||||
- `ec-ts` parses ATSC PSIP at the table layer (`MGT`, `TVCT/CVCT`, `STT`, `RRT`, `EIT`, `ETT`),
|
||||
including `EIT` / `ETT` on the PIDs advertised by `MGT`.
|
||||
- Current discovery promotion uses `PAT` plus `VCT` fields; the rest of PSIP is parsed and preserved
|
||||
for inspection, validation, and future policy rather than guessed into the stream key.
|
||||
|
||||
## Time synchronization
|
||||
|
||||
- Chunk boundaries are derived from PCR and, when available, broadcast UTC (ATSC STT / DVB TDT/TOT).
|
||||
- ATSC STT is interpreted as GPS time plus offset correction, then converted to Unix time for chunk
|
||||
anchoring and diagnostics.
|
||||
- Unsynced sources remain source-scoped until broadcast time is present.
|
||||
- Discontinuities force a new chunk group boundary.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
Optional overrides:
|
||||
|
||||
```sh
|
||||
EVERY_CHANNEL_FORGE_HOST=https://forge.every.channel \
|
||||
EVERY_CHANNEL_FORGE_HOST=https://git.every.channel \
|
||||
EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \
|
||||
EVERY_CHANNEL_PROTECTED_BRANCH=main \
|
||||
EVERY_CHANNEL_REQUIRED_CHECKS="ci-gates / checks" \
|
||||
|
|
@ -31,7 +31,7 @@ EVERY_CHANNEL_REQUIRED_APPROVALS=1 \
|
|||
Token source order:
|
||||
|
||||
1. `EVERY_CHANNEL_FORGE_TOKEN` / `FORGE_TOKEN` / `CODEBERG_TOKEN` env var
|
||||
2. `secrets/forge-token.age` (preferred) via `agenix` or `age`
|
||||
3. `secrets/codeberg-token.age` (compat) via `agenix` or `age`
|
||||
2. `secrets/forgejo-api-token.age` (preferred) via `agenix` or `age`
|
||||
3. `secrets/forge-token.age` or `secrets/codeberg-token.age` (compat) via `agenix` or `age`
|
||||
|
||||
The token must have repository admin scope to edit branch protection.
|
||||
|
|
|
|||
46
docs/DEPLOY_ECP_FORGE.md
Normal file
46
docs/DEPLOY_ECP_FORGE.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Sovereign Deploy: `ecp-forge`
|
||||
|
||||
This repository owns deployment of `git.every.channel` (Hetzner 300TB host).
|
||||
|
||||
## Requirements
|
||||
|
||||
- SSH access to `root@git.every.channel`.
|
||||
- Local key that matches host `authorized_keys` (default: `~/.ssh/id_ed25519`).
|
||||
- `nix` with flakes enabled.
|
||||
|
||||
## Deploy
|
||||
|
||||
```sh
|
||||
./scripts/deploy-ecp-forge.sh
|
||||
```
|
||||
|
||||
For the OP Stack operator path and observation-rail validation, see:
|
||||
|
||||
```sh
|
||||
cat docs/OP_STACK_ECP_FORGE.md
|
||||
```
|
||||
|
||||
Equivalent:
|
||||
|
||||
```sh
|
||||
NIX_SSHOPTS="-o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519" \
|
||||
nix run nixpkgs#nixos-rebuild -- \
|
||||
--flake .#ecp-forge \
|
||||
--target-host root@git.every.channel \
|
||||
--build-host root@git.every.channel \
|
||||
--use-remote-sudo \
|
||||
switch
|
||||
```
|
||||
|
||||
## Overrides
|
||||
|
||||
- `EVERY_CHANNEL_FORGE_TARGET_HOST` (default `root@git.every.channel`)
|
||||
- `EVERY_CHANNEL_FORGE_BUILD_HOST` (default same as target)
|
||||
- `EVERY_CHANNEL_FORGE_SSH_IDENTITY` (default `~/.ssh/id_ed25519`)
|
||||
|
||||
## Verify
|
||||
|
||||
```sh
|
||||
ssh -o BatchMode=yes -o IdentityAgent=none -i ~/.ssh/id_ed25519 root@git.every.channel \
|
||||
'hostnamectl --static; systemctl is-active forgejo caddy every-channel-netboot-stage every-channel-netboot'
|
||||
```
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Primary host:
|
||||
|
||||
- Forgejo (`origin`)
|
||||
- Forgejo (`origin`, `git.every.channel`)
|
||||
|
||||
Mirrors (push-only):
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ Codeberg and GitHub are distribution mirrors only. CI/actions should run on Forg
|
|||
|
||||
Defaults:
|
||||
|
||||
- `origin`: `git@forge.every.channel:every-channel/every.channel.git`
|
||||
- `origin`: `ssh://forgejo@git.every.channel:2222/every-channel/every.channel.git`
|
||||
- `mirror-codeberg`: `git@codeberg.org:every-channel/every.channel.git`
|
||||
- `mirror-github`: `git@github.com:every-channel/every.channel.git`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,88 @@
|
|||
# NUC Fleet Netboot (Unifi + ProxyDHCP)
|
||||
# NUC Fleet Netboot (Unifi)
|
||||
|
||||
This runbook provisions x86_64 NUCs from runner netboot artifacts without USB image flashing.
|
||||
|
||||
It uses:
|
||||
Supported modes:
|
||||
|
||||
- Unifi DHCP for IP leases.
|
||||
- Local `dnsmasq` ProxyDHCP for PXE/iPXE bootfile logic.
|
||||
- Local HTTP + TFTP service for boot artifacts.
|
||||
|
||||
## Why ProxyDHCP
|
||||
|
||||
iPXE commonly needs two boot stages:
|
||||
|
||||
1. firmware PXE -> `ipxe.efi`
|
||||
2. iPXE -> `netboot.ipxe`
|
||||
|
||||
If DHCP always returns `ipxe.efi`, clients can loop forever. ProxyDHCP handles stage-specific boot responses cleanly while leaving Unifi as the DHCP lease server.
|
||||
- ProxyDHCP mode: recommended when you want automatic stage-1/2 iPXE handling.
|
||||
- UniFi-only mode: DHCP options 66/67 in UniFi, no ProxyDHCP.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Linux boot server on the same VLAN/L2 domain as the NUCs.
|
||||
- Unifi network with normal DHCP enabled.
|
||||
- Linux boot server on the same VLAN/L2 domain as the NUCs.
|
||||
- Unifi network with DHCP enabled.
|
||||
- Local DNS record on that VLAN: `boot.every.channel -> <boot-server-ip>`.
|
||||
- `curl`, `tar`, `python3`, `dnsmasq` installed on the boot server.
|
||||
- Runner netboot artifact already published to Forgejo Releases (or available as a local tarball).
|
||||
- For UniFi-only mode with reliable chainloading: `git` and `make` to build embedded iPXE.
|
||||
- `openssl` (or equivalent) if you want generated chain tokens.
|
||||
- Runner netboot artifact published to Forgejo Releases (or available as local tarball).
|
||||
|
||||
## 1) Stage artifacts
|
||||
## Persistent NixOS service (recommended)
|
||||
|
||||
From repository root on the boot server:
|
||||
Instead of running scripts manually, use the exported NixOS module and keep netboot
|
||||
staging/serving declarative:
|
||||
|
||||
```nix
|
||||
{
|
||||
imports = [ every-channel.nixosModules.ec-netboot ];
|
||||
|
||||
services.every-channel.netboot = {
|
||||
enable = true;
|
||||
listenIP = "10.20.30.2";
|
||||
interface = "enp195s0";
|
||||
hostname = "boot.every.channel";
|
||||
tftpBootFilename = "ec-ipxe.efi";
|
||||
httpAllowedCIDRs = [ "10.20.30.0/24" ];
|
||||
chainTokenFile = "/run/agenix/every-channel-netboot-chain-token";
|
||||
|
||||
# UniFi-only mode by default (no ProxyDHCP):
|
||||
proxyDhcp.enable = false;
|
||||
|
||||
release.host = "https://git.every.channel";
|
||||
release.repo = "every-channel/every.channel";
|
||||
# release.tag = "boot-v2026.03.02"; # optional pin
|
||||
# release.tokenFile = "/run/agenix/forgejo-api-token"; # optional private repo token
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Operational commands:
|
||||
|
||||
```sh
|
||||
sudo systemctl start every-channel-netboot-stage.service
|
||||
sudo systemctl restart every-channel-netboot.service
|
||||
sudo systemctl status every-channel-netboot.service
|
||||
```
|
||||
|
||||
If you prefer ProxyDHCP mode:
|
||||
|
||||
```nix
|
||||
services.every-channel.netboot.proxyDhcp.enable = true;
|
||||
services.every-channel.netboot.proxyDhcp.subnet = "10.20.30.0/24";
|
||||
```
|
||||
|
||||
## 1) Build embedded iPXE (UniFi-only mode)
|
||||
|
||||
This removes iPXE boot loops without requiring ProxyDHCP.
|
||||
|
||||
```sh
|
||||
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
||||
EVERY_CHANNEL_NETBOOT_HTTP_PORT=8080 \
|
||||
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="$(openssl rand -hex 16)" \
|
||||
./scripts/netboot-build-ipxe.sh
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
- `tmp/netboot/tftp/ec-ipxe.efi` (use this as DHCP option 67 filename)
|
||||
|
||||
## 2) Stage runner netboot artifacts
|
||||
|
||||
```sh
|
||||
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
||||
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
||||
EVERY_CHANNEL_NETBOOT_IPXE_EFI_PATH=tmp/netboot/tftp/ec-ipxe.efi \
|
||||
EVERY_CHANNEL_NETBOOT_IPXE_EFI_FILENAME=ec-ipxe.efi \
|
||||
./scripts/netboot-stage.sh
|
||||
```
|
||||
|
||||
|
|
@ -38,16 +91,31 @@ Optional inputs:
|
|||
- `EVERY_CHANNEL_NETBOOT_RELEASE_TAG=boot-v2026.02.28`
|
||||
- `EVERY_CHANNEL_NETBOOT_TARBALL=/path/to/ec-runner-x86_64-netboot-....tar.gz`
|
||||
- `EVERY_CHANNEL_FORGE_TOKEN=<token>` for private releases
|
||||
- `EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel`
|
||||
- `EVERY_CHANNEL_NETBOOT_ALLOW_REMOTE_IPXE=true` only if you intentionally want to download iPXE from URL
|
||||
- `EVERY_CHANNEL_IPXE_EFI_SHA256=<sha256>` to pin iPXE binary integrity
|
||||
|
||||
This stages:
|
||||
|
||||
- `tmp/netboot/http/{kernel,initrd,netboot.ipxe}`
|
||||
- `tmp/netboot/tftp/ipxe.efi`
|
||||
- `tmp/netboot/tftp/ec-ipxe.efi`
|
||||
|
||||
## 2) Serve HTTP + TFTP + ProxyDHCP
|
||||
## 3) Serve HTTP + TFTP
|
||||
|
||||
Example (replace values for your VLAN):
|
||||
UniFi-only mode (no ProxyDHCP):
|
||||
|
||||
```sh
|
||||
sudo \
|
||||
EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 \
|
||||
EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 \
|
||||
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
||||
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
||||
EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS=10.20.30.0/24 \
|
||||
EVERY_CHANNEL_NETBOOT_PROXY_DHCP=false \
|
||||
EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME=ec-ipxe.efi \
|
||||
./scripts/netboot-serve.sh
|
||||
```
|
||||
|
||||
ProxyDHCP mode:
|
||||
|
||||
```sh
|
||||
sudo \
|
||||
|
|
@ -55,48 +123,40 @@ sudo \
|
|||
EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 \
|
||||
EVERY_CHANNEL_NETBOOT_PROXY_SUBNET=10.20.30.0/24 \
|
||||
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
||||
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
||||
EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS=10.20.30.0/24 \
|
||||
EVERY_CHANNEL_NETBOOT_PROXY_DHCP=true \
|
||||
EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME=ec-ipxe.efi \
|
||||
./scripts/netboot-serve.sh
|
||||
```
|
||||
|
||||
Notes:
|
||||
## 4) UniFi settings (you do this)
|
||||
|
||||
- Keep this process running during provisioning.
|
||||
- Do not set Unifi DHCP bootfile options while this proxy mode is active.
|
||||
- Ensure `boot.every.channel` resolves to the boot server IP from NUC clients.
|
||||
UniFi-only mode:
|
||||
|
||||
## 3) Unifi / NUC settings
|
||||
- `Network Boot`: enabled
|
||||
- `Server`: `boot.every.channel` (or boot server IP)
|
||||
- `Filename`: `ec-ipxe.efi`
|
||||
- `TFTP Server`: `boot.every.channel`
|
||||
|
||||
Unifi:
|
||||
ProxyDHCP mode:
|
||||
|
||||
- Keep DHCP enabled for the provisioning VLAN.
|
||||
- Leave DHCP boot/TFTP overrides unset when using `netboot-serve.sh`.
|
||||
- Create/verify local DNS host override: `boot.every.channel -> <boot-server-ip>`.
|
||||
- leave UniFi boot/TFTP options unset.
|
||||
|
||||
NUC BIOS:
|
||||
|
||||
- Enable UEFI network boot (IPv4 PXE).
|
||||
- Disable Legacy/CSM if possible.
|
||||
- Put network boot before disk for first install cycle.
|
||||
- Enable UEFI PXE boot.
|
||||
- Disable Legacy/CSM where possible.
|
||||
- Put network boot first for initial install.
|
||||
|
||||
## 4) Provision the fleet
|
||||
## Security hardening
|
||||
|
||||
1. Boot each NUC on the provisioning VLAN.
|
||||
2. PXE will chainload into iPXE and then runner `netboot.ipxe`.
|
||||
3. Complete install/bootstrap flow on each node.
|
||||
4. After successful install, switch boot order back to local disk.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Symptom: iPXE loop (`ipxe.efi` repeatedly)
|
||||
- Cause: static DHCP bootfile without iPXE-aware logic.
|
||||
- Fix: use ProxyDHCP flow (`netboot-serve.sh`) or set conditional DHCP rules.
|
||||
- Symptom: NUC gets IP but never downloads boot artifacts
|
||||
- Verify firewall allows UDP 67/68, UDP 69, and TCP 8080 between NUCs and boot server.
|
||||
- Symptom: no `dnsmasq` offers seen
|
||||
- Verify `EVERY_CHANNEL_NETBOOT_INTERFACE` and `EVERY_CHANNEL_NETBOOT_PROXY_SUBNET`.
|
||||
|
||||
## Security / networking
|
||||
|
||||
- Tailscale is not required for provisioning.
|
||||
- Keep the provisioning VLAN isolated from regular clients.
|
||||
- Stop `netboot-serve.sh` when rollout is complete.
|
||||
- Keep provisioning on an isolated VLAN.
|
||||
- Allow only required ports from NUC VLAN to boot server: UDP 69, TCP 8080 (and DHCP if ProxyDHCP mode).
|
||||
- Keep provisioning services up only during rollout, then stop them.
|
||||
- Use `EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS` to limit HTTP artifact access to NUC subnet(s).
|
||||
- Use `EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN` so only tokened iPXE chain requests receive `netboot.ipxe`.
|
||||
- Use checksum verification in `netboot-stage.sh` (enabled by default when release has `SHA256SUMS.txt`).
|
||||
- `netboot-stage.sh` now defaults to local iPXE binaries; remote URL download requires explicit opt-in.
|
||||
- Prefer embedded `ec-ipxe.efi` with fixed chain target over generic unsigned internet binaries.
|
||||
- If Secure Boot is required, use signed boot chain and keys for your environment (outside this basic runbook).
|
||||
|
|
|
|||
110
docs/OP_STACK_ECP_FORGE.md
Normal file
110
docs/OP_STACK_ECP_FORGE.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# OP Stack on `ecp-forge`
|
||||
|
||||
This runbook covers the repo-owned OP Stack testnet surface introduced by [ECP-0093](/Users/conradev/Projects/every.channel/evolution/proposals/ECP-0093-ecp-forge-op-stack-sepolia-observation-testnet.md).
|
||||
|
||||
## Scope
|
||||
|
||||
- `ecp-forge` runs the every.channel OP Stack services with pinned official container images.
|
||||
- The chain is Sepolia-anchored and private by default on the RPC side.
|
||||
- Application-level consensus lives in the observation rail:
|
||||
- [EveryChannelWitnessRegistry.sol](/Users/conradev/Projects/every.channel/contracts/EveryChannelWitnessRegistry.sol)
|
||||
- [EveryChannelObservationLedger.sol](/Users/conradev/Projects/every.channel/contracts/EveryChannelObservationLedger.sol)
|
||||
|
||||
## Required inputs
|
||||
|
||||
- `secrets/op-stack-sepolia-private-key.age`
|
||||
- Sepolia operator key for `op-deployer`, `op-node`, `op-batcher`, and `op-proposer`.
|
||||
- `secrets/op-stack-challenger-prestate.bin.gz.age`
|
||||
- Cannon absolute prestate artifact for `op-challenger`.
|
||||
|
||||
If the private key secret is absent, `services.every-channel.op-stack.enable = false` on `ecp-forge`.
|
||||
If the prestate artifact is absent, `op-challenger` and `op-dispute-mon` stay disabled even when the
|
||||
core rollup services are enabled.
|
||||
|
||||
## Local validation
|
||||
|
||||
Contracts:
|
||||
|
||||
```sh
|
||||
nix shell .#foundry .#solc -c forge test -vv
|
||||
```
|
||||
|
||||
Real archive data through Anvil:
|
||||
|
||||
```sh
|
||||
nix shell .#foundry .#solc nixpkgs#jq nixpkgs#openssh nixpkgs#curl -c ./scripts/op-stack/anvil-reality-smoke.sh
|
||||
```
|
||||
|
||||
The smoke script:
|
||||
|
||||
- deploys the witness registry and observation ledger to Anvil,
|
||||
- reads a real archive JSONL entry from `root@git.every.channel`,
|
||||
- derives `stream_hash`, `epoch_hash`, `locator_hash`, and `observation_hash`,
|
||||
- finalizes the observation with two Anvil witnesses.
|
||||
|
||||
Default remote source:
|
||||
|
||||
- `/var/lib/every-channel/manifests/la-cbs/video0.m4s.jsonl`
|
||||
|
||||
Output artifact:
|
||||
|
||||
- `test-results/anvil-reality-smoke.json`
|
||||
|
||||
## Deploy
|
||||
|
||||
```sh
|
||||
./scripts/deploy-ecp-forge.sh
|
||||
```
|
||||
|
||||
The host bootstrap service is:
|
||||
|
||||
- `every-channel-op-stack-bootstrap.service`
|
||||
|
||||
It writes runtime state under:
|
||||
|
||||
- `/var/lib/every-channel/op-stack`
|
||||
|
||||
Key generated artifacts:
|
||||
|
||||
- `/var/lib/every-channel/op-stack/deployment.json`
|
||||
- `/var/lib/every-channel/op-stack/sequencer/genesis.json`
|
||||
- `/var/lib/every-channel/op-stack/sequencer/rollup.json`
|
||||
|
||||
## Verify
|
||||
|
||||
Core services:
|
||||
|
||||
```sh
|
||||
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
|
||||
'systemctl is-active every-channel-op-stack-bootstrap podman-every-channel-op-geth podman-every-channel-op-node podman-every-channel-op-batcher podman-every-channel-op-proposer'
|
||||
```
|
||||
|
||||
Full stack including challenger/dispute monitor:
|
||||
|
||||
```sh
|
||||
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
|
||||
'systemctl is-active podman-every-channel-op-challenger podman-every-channel-op-dispute-mon'
|
||||
```
|
||||
|
||||
Bootstrap outputs:
|
||||
|
||||
```sh
|
||||
ssh -o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@git.every.channel \
|
||||
'jq . /var/lib/every-channel/op-stack/deployment.json'
|
||||
```
|
||||
|
||||
## Contract deployment on the rollup
|
||||
|
||||
Once the rollup RPC is live, deploy the observation rail to the L2 RPC:
|
||||
|
||||
```sh
|
||||
EVERY_CHANNEL_RPC_URL=http://127.0.0.1:8545 \
|
||||
EVERY_CHANNEL_PRIVATE_KEY_FILE=/path/to/private-key \
|
||||
./scripts/op-stack/deploy-observation-ledger.sh
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- `op-geth` and `op-node` RPC surfaces bind to `127.0.0.1` on `ecp-forge`.
|
||||
- The public firewall opening is only for the `op-node` P2P port.
|
||||
- The bootstrap uses `op-deployer/v0.6.0-rc.3` by default and official OP Labs container images.
|
||||
|
|
@ -9,6 +9,12 @@ This repo exports reproducible NixOS runner configurations via flake outputs:
|
|||
- `nixosConfigurations.ec-runner-x86_64-iso`
|
||||
- `nixosConfigurations.ec-runner-aarch64-sdimage`
|
||||
|
||||
It also exports reusable NixOS modules:
|
||||
|
||||
- `nixosModules.ec-runner`
|
||||
- `nixosModules.ec-node`
|
||||
- `nixosModules.ec-netboot` (persistent HTTP/TFTP netboot stage+serve service)
|
||||
|
||||
The runner OS exposes this repo's flake source inside the system at:
|
||||
|
||||
- `/etc/every-channel/flake`
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ cd apps/tauri
|
|||
cargo tauri dev
|
||||
```
|
||||
|
||||
If you want to run the desktop app directly from Cargo against the bundled frontend instead of the
|
||||
dev server, run:
|
||||
|
||||
```sh
|
||||
EVERY_CHANNEL_ROOT=$PWD cargo run -p ec-tauri --features custom-protocol
|
||||
```
|
||||
|
||||
If you want deterministic transcoding instead of stream copy:
|
||||
|
||||
```sh
|
||||
|
|
@ -31,6 +38,40 @@ EVERY_CHANNEL_IROH_DISCOVERY=dht,mdns cargo tauri dev
|
|||
|
||||
In the Tauri app, use **Add stream** to add an HDHomeRun host, a direct HLS URL, or a yt-dlp supported URL (e.g. YouTube Live). The flow rejects non-live sources.
|
||||
|
||||
`https://www.nbc.com/watch/...` URLs are also supported in the Tauri app. This path is
|
||||
browser-backed:
|
||||
|
||||
- on macOS, the app first opens an in-app Tauri webview backed by `WKWebView`
|
||||
- NBC / Adobe Pass authentication stays in that native app window, including popup sign-in flows
|
||||
- if native playback cannot become ready, the app falls back to the existing external Chrome path
|
||||
- once playback is live, the app captures rendered video frames and feeds them into the existing
|
||||
ffmpeg ladder
|
||||
|
||||
Notes:
|
||||
|
||||
- the first run may require you to finish your MVPD login in the native app window or, if native
|
||||
playback falls back, in the launched Chrome window
|
||||
- on macOS, the default native webview data directory is app-local; override it with
|
||||
`EVERY_CHANNEL_NBC_WEBVIEW_DATA_DIR=/path/to/webview-data`
|
||||
- for future unattended runs with a warm session, set `EVERY_CHANNEL_NBC_HIDE_WINDOWS=1` to keep
|
||||
the native NBC webviews hidden; if interactive auth is needed, the app will surface the window
|
||||
instead of silently hanging
|
||||
- the desktop app also exposes `bootstrap_nbc_auth`; in the Add menu, use `Bootstrap selected NBC`
|
||||
or `Bootstrap pasted NBC URL` to warm the hidden session before later playback runs
|
||||
- the fallback Chrome profile directory is app-local; override it with
|
||||
`EVERY_CHANNEL_NBC_PROFILE_DIR=/path/to/profile`
|
||||
- override the Chrome binary with `EVERY_CHANNEL_NBC_CHROME_PATH=/path/to/chrome`
|
||||
- when `EVERY_CHANNEL_NBC_HIDE_WINDOWS=1` is set, the app refuses visible Chrome fallback if the
|
||||
native path fails
|
||||
- the app also pulls NBC's public live guide before auth so browseable NBC channel rows can
|
||||
appear in the Channels list; override that guide shaping with
|
||||
`EVERY_CHANNEL_NBC_PUBLIC_TIMEZONE`, `EVERY_CHANNEL_NBC_PUBLIC_NBC_AFFILIATE`,
|
||||
`EVERY_CHANNEL_NBC_PUBLIC_TELEMUNDO_AFFILIATE`, and
|
||||
`EVERY_CHANNEL_NBC_PUBLIC_BROADCAST_TYPE`
|
||||
- capture is currently video-first; audio is not guaranteed in the first cut
|
||||
- adjust startup timeout / capture rate with `EVERY_CHANNEL_NBC_CAPTURE_TIMEOUT_SECS`,
|
||||
`EVERY_CHANNEL_NBC_CAPTURE_FPS`, and `EVERY_CHANNEL_NBC_CAPTURE_QUALITY`
|
||||
|
||||
Linux DVB sources can be added with a URL like:
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Status: Implemented
|
||||
|
||||
Note: Persistent declarative host operation is specified in ECP-0083.
|
||||
|
||||
## Context
|
||||
|
||||
Runner netboot artifacts now publish from CI, but there is no repository-native operating path for fleet provisioning on common prosumer networks (for example Unifi VLANs).
|
||||
|
|
@ -17,6 +19,12 @@ Unifi DHCP can expose next-server/bootfile settings, but iPXE chainloading often
|
|||
2. Keep Unifi DHCP as the IP authority; use ProxyDHCP only to supply bootfile logic.
|
||||
3. Document a concrete NUC rollout sequence for same-VLAN provisioning.
|
||||
4. Keep dependencies minimal (`curl`, `tar`, `python3`, `dnsmasq`) and avoid requiring image flashing workflows.
|
||||
5. Support an optional UniFi-only mode by providing an embedded-script iPXE build path (`ec-ipxe.efi`) so clients can chainload without DHCP conditional logic.
|
||||
6. Verify release artifact integrity during staging when `SHA256SUMS.txt` is published.
|
||||
7. Harden serving/staging defaults:
|
||||
- default to local iPXE artifacts (remote iPXE download requires explicit opt-in),
|
||||
- support optional chain token protection for `netboot.ipxe`,
|
||||
- support HTTP CIDR allowlists for artifact serving.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
# ECP-0083: Declarative Netboot Service Module
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Context
|
||||
|
||||
ECP-0082 added script-driven netboot staging and serving for UniFi/ProxyDHCP fleets. That path works, but it is still operator-session driven (`tmux`, manual env vars, manual restart order), which is fragile for sustained fleet bring-up.
|
||||
|
||||
The constitution favors explicit, reviewable infrastructure definitions. Netboot delivery should be operated as a normal NixOS service with stable options, systemd lifecycle, and auditable host config.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Add a reusable NixOS module at `nix/modules/ec-netboot.nix` exported as `nixosModules.ec-netboot`.
|
||||
2. Define a first-class `services.every-channel.netboot` option tree for:
|
||||
- UniFi-only mode (default, no ProxyDHCP),
|
||||
- optional ProxyDHCP mode,
|
||||
- release source pinning (host/repo/tag/local tarball/token file),
|
||||
- iPXE strategy (embedded build, local file, or explicit remote download),
|
||||
- security controls (chain token file, HTTP CIDR allowlist).
|
||||
3. Run persistent systemd units:
|
||||
- `every-channel-netboot-ipxe` (oneshot, optional embedded EFI build),
|
||||
- `every-channel-netboot-stage` (oneshot artifact staging),
|
||||
- `every-channel-netboot` (long-running HTTP+TFTP service).
|
||||
4. Add tmpfiles and firewall wiring in-module so host configs remain concise and reversible.
|
||||
5. Keep existing scripts as execution primitives to avoid duplicate logic and preserve local/manual fallback operations.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep scripts only. Rejected because startup order, secret injection, and restart behavior remain ad-hoc.
|
||||
- Implement host-specific module logic only in `key.store`. Rejected because this behavior is core `every.channel` netboot operations and should be reusable across hosts.
|
||||
- Replace scripts with a brand new daemon immediately. Rejected to keep rollout incremental and avoid avoidable regressions.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
- Rollout:
|
||||
- import `every-channel.nixosModules.ec-netboot` on the boot host,
|
||||
- set `services.every-channel.netboot.*` options,
|
||||
- activate and verify `every-channel-netboot-stage` then `every-channel-netboot`.
|
||||
- Teardown:
|
||||
- disable `services.every-channel.netboot.enable`,
|
||||
- remove host option stanza,
|
||||
- fall back to manual script operation from `docs/NUC_UNIFI_NETBOOT.md` if needed.
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# ECP-0084: Sovereign `ecp-forge` Host Deploy from every.channel
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Context
|
||||
|
||||
`git.every.channel` (Hetzner 300TB host) has been operated from external infra repos. That creates coupling and weakens operational independence for every.channel infrastructure changes, especially netboot/PXE and archive workflows.
|
||||
|
||||
The constitutional direction is explicit repository ownership over its infrastructure path. every.channel should be able to deploy its own forge host from this repository, with age/agenix material stored here.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Add a sovereign `nixosConfigurations.ecp-forge` target to this repository.
|
||||
2. Keep the forge role (`services.forgejo`, `services.caddy`) and archive role (`services.every-channel.ec-node`) in that host target.
|
||||
3. Enable persistent netboot from this repository using `services.every-channel.netboot`, with local sovereign tarball staging as the default source path.
|
||||
4. Keep UniFi-only mode as default (`proxyDhcp.enable = false`) to avoid cross-domain DHCP coupling.
|
||||
5. Store host-consumed runtime secrets in this repository (`secrets/*.age`) and decrypt on-host via `agenix`.
|
||||
6. Deploy directly from this repository to `git.every.channel`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Continue deploying `git.every.channel` from shared infra repos. Rejected due ownership/coupling drift.
|
||||
- Keep runtime-only netboot scripts on host. Rejected because boot resilience should survive reboot and config rebuilds.
|
||||
- Move to ProxyDHCP-first by default. Rejected for now to keep DHCP authority in UniFi.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
- Rollout:
|
||||
- build/evaluate `.#nixosConfigurations.ecp-forge`,
|
||||
- deploy from every.channel to `git.every.channel`,
|
||||
- verify `every-channel-netboot-stage` and `every-channel-netboot`.
|
||||
- Teardown:
|
||||
- disable `services.every-channel.netboot.enable` in `nix/nixos/ecp-forge.nix`,
|
||||
- redeploy,
|
||||
- fall back to manual script flow (`docs/NUC_UNIFI_NETBOOT.md`) if required.
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# ECP-0085: Enable Archive Auto-Worker on Sovereign `ecp-forge`
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Context
|
||||
|
||||
`ecp-forge` currently runs Forgejo and netboot services from `every.channel`, but archival ingestion is not active. Historical archive/manifests exist on host storage, yet no `every-channel-wt-archive*` unit is running, so stream persistence stalls.
|
||||
|
||||
The constitutional direction favors an operational archive network. The sovereign forge host should continuously ingest public streams into durable storage.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Enable `services.every-channel.ec-node` on `ecp-forge`.
|
||||
2. Run archive ingestion mode (`archive.enable = true`) with no broadcast publish role.
|
||||
3. Use:
|
||||
- `archive.outputDir = "/tank/every-channel/archive"` (large durable ZFS storage),
|
||||
- `archive.manifestDir = "/var/lib/every-channel/manifests"` (existing manifest path continuity).
|
||||
4. Keep `archive.serve.enable = false` for now; this change is ingest-only.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep archive workers manual/ephemeral. Rejected because ingestion must survive reboot/redeploy.
|
||||
- Move manifests to `/tank` in this change. Deferred to avoid migration risk while restoring ingestion first.
|
||||
- Enable replay serving (`wt-archive-serve`) immediately. Rejected for scope control; ingest health first.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
- Rollout:
|
||||
- deploy `.#nixosConfigurations.ecp-forge`,
|
||||
- verify `every-channel-wt-archive-auto` is active,
|
||||
- verify journal shows polling/worker spawn and manifests update.
|
||||
- Teardown:
|
||||
- set `services.every-channel.ec-node.archive.enable = false`,
|
||||
- deploy,
|
||||
- preserve existing on-disk archive/manifests data.
|
||||
35
evolution/proposals/ECP-0086-web-watcher-jitter-budget.md
Normal file
35
evolution/proposals/ECP-0086-web-watcher-jitter-budget.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ECP-0086: Web Watcher Jitter Budget Override
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Problem
|
||||
|
||||
Browser live playback on `every.channel` stays connected but still cuts in and out. A Chrome browser repro against `https://every.channel/watch?url=https%3A%2F%2Fcdn.moq.dev%2Fanon&name=la-nbc` showed no remounts or request failures, but the `@moq/watch@0.2.0` player emitted continuous `skipping slow group` warnings plus frequent small seek corrections.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Keep the existing `@moq/watch` integration and WebTransport-only path.
|
||||
- Avoid forking upstream player code for a site-level playback tuning change.
|
||||
- Keep the fix reversible and local to the web app wiring.
|
||||
|
||||
## Decision
|
||||
|
||||
Set the web watcher's `jitter` attribute to `750` milliseconds in `apps/web/app.js` before connect.
|
||||
|
||||
Browser evidence for the same live stream showed:
|
||||
|
||||
- default `jitter=100`: `2762` warnings over the sample, including `324` seek corrections, final `readyState=1`
|
||||
- forced `jitter=750`: `2482` warnings over the sample, including `75` seek corrections, final `readyState=4`
|
||||
|
||||
This does not eliminate upstream `skipping slow group` churn entirely, but it substantially reduces the seek-thrash that most directly surfaces as visible cut-in/cut-out playback.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Leave the default `100ms` jitter budget. Rejected because the browser repro showed sustained seek churn under live playback.
|
||||
- Raise jitter further (for example `1500ms`). Rejected for now because it reduced some warnings but regressed playback advancement more than `750ms` in the same sample.
|
||||
- Fork `@moq/watch`. Rejected because the exposed `jitter` control was sufficient for a first mitigation.
|
||||
|
||||
## Rollout / Teardown
|
||||
|
||||
- Deploy the `jitter=750` override and validate live watch continuity in Chrome against public relay streams.
|
||||
- If the extra latency is unacceptable or upstream player behavior changes, remove the override and fall back to the library default.
|
||||
40
evolution/proposals/ECP-0087-lan-capable-ipxe-qemu-module.md
Normal file
40
evolution/proposals/ECP-0087-lan-capable-ipxe-qemu-module.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# ECP-0087: LAN-Capable iPXE/QEMU VM Module
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Context
|
||||
|
||||
`every.channel` already has reusable runner and netboot modules, but hosts that want a continuously-running iPXE test VM still have to carry ad hoc QEMU glue. That makes boot-path verification and downstream reuse harder, and it prevents hosts from declaratively opting into a LAN-visible guest for tuner discovery.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Add `nixosModules.ec-ipxe-qemu` at `nix/modules/ec-ipxe-qemu.nix`.
|
||||
2. Add a dedicated publisher guest module/output pair:
|
||||
- `nixosModules.ec-publisher-guest`
|
||||
- `nixosConfigurations.ec-publisher-x86_64`
|
||||
- `nixosConfigurations.ec-publisher-x86_64-netboot`
|
||||
This keeps the VM path explicitly publisher-oriented while leaving host-specific tuner/broadcast choices to downstream configs.
|
||||
3. Define `services.every-channel.ipxe-qemu.*` options for:
|
||||
- persistent qcow2/state directory handling,
|
||||
- user-mode iPXE boot networking that chains to a configurable internet boot URL,
|
||||
- optional second NIC via `macvtap` for non-disruptive LAN presence,
|
||||
- guest sizing and raw QEMU argument overrides.
|
||||
4. Run the guest as a persistent systemd service (`every-channel-ipxe-qemu`) with restart-on-exit semantics so host restarts or config switches naturally refresh the in-memory booted VM.
|
||||
5. Enable one conservative instance on `ecp-forge` using the user-mode boot path only, so the module is exercised in-repo without assuming a local tuner LAN on the forge host.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep host-specific shell glue outside `every.channel`. Rejected because downstream hosts cannot reuse or review the boot path as a first-class module.
|
||||
- Require a Linux bridge on the host for LAN access. Rejected for now because it is more disruptive than `macvtap` and unnecessary for an initial deployment.
|
||||
- Boot only with QEMU user networking. Rejected because tuner discovery needs a real LAN attachment on some hosts.
|
||||
|
||||
## Rollout / Teardown
|
||||
|
||||
- Rollout:
|
||||
- import `nixosModules.ec-ipxe-qemu`,
|
||||
- enable `services.every-channel.ipxe-qemu`,
|
||||
- set `lan.enable = true` plus `lan.macvtap.interface` on hosts that need LAN discovery.
|
||||
- Teardown:
|
||||
- disable `services.every-channel.ipxe-qemu.enable`,
|
||||
- remove host options,
|
||||
- fall back to ad hoc QEMU or direct host publishers if needed.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ECP-0088: Public RPC/NFS Hardening for `ecp-forge`
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Context
|
||||
|
||||
`ecp-forge` exports `/tank` over NFS for private consumers, but the host firewall also exposed `rpcbind` (`111/tcp,udp`) and NFS (`2049/tcp,udp`) on the public Hetzner address. CERT-Bund flagged the host because public `rpcbind` allowed internet enumeration of registered RPC services.
|
||||
|
||||
The current exports already constrain clients to private address space, so the exposure is a firewall boundary issue rather than a requirement for public access.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Remove public firewall allowances for `111/tcp,udp` and `2049/tcp,udp` on `ecp-forge`.
|
||||
2. Keep NFS enabled for trusted/private paths, including the existing `tailscale0` trusted interface and private-source exports.
|
||||
3. Treat public RPC/NFS exposure on forge hosts as an anti-pattern unless a later ECP explicitly justifies it.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Disable NFS entirely. Rejected because `/tank` export remains useful for private consumers.
|
||||
- Keep public ports open and rely only on `/etc/exports` CIDR restrictions. Rejected because `rpcbind` enumeration is itself enough to trigger abuse notifications and increases attack surface.
|
||||
- Add bespoke public-interface firewall exceptions per private CIDR. Rejected because `ecp-forge` already has a trusted overlay path and does not need public-interface exposure for NFS.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
- Rollout:
|
||||
- evaluate `.#nixosConfigurations.ecp-forge`,
|
||||
- deploy `ecp-forge`,
|
||||
- verify `rpcbind`/NFS are no longer reachable on the public IP.
|
||||
- Teardown:
|
||||
- restore the public firewall allowances only with a replacement ECP that documents the requirement and compensating controls.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# ECP-0089: Broadcast-Scoped Discovery Identity For Gossip And DHT
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Decision
|
||||
|
||||
When `ec-node` needs to synthesize a default `stream_id` for publish/announce flows, it will prefer a broadcast-scoped identifier derived from channel metadata over a source-scoped identifier.
|
||||
|
||||
Current rule:
|
||||
|
||||
- If the selected channel yields a usable broadcast identity, generate `ec/stream/v1/broadcast/...`.
|
||||
- Otherwise keep the existing source-scoped fallback `ec/stream/v1/source/...`.
|
||||
- Explicit `--stream-id` continues to override both behaviors.
|
||||
|
||||
For HDHomeRun lineup sources, the usable broadcast identity is assembled from the channel record when present:
|
||||
|
||||
- `standard = atsc`
|
||||
- `program_number` from lineup `ProgramNumber`/`ProgramID` when available
|
||||
- `virtual_channel` from `GuideNumber`
|
||||
- `callsign` from typed lineup metadata or channel name fallback
|
||||
- optional `region` / `frequency` hints when present
|
||||
|
||||
## Motivation
|
||||
|
||||
Today identical OTA channels announced from different ingest nodes often diverge because the default identity is source-scoped (`hdhr`, device id, local channel selection). That defeats gossip/DHT dedupe and makes the network treat equivalent broadcasts as unrelated streams.
|
||||
|
||||
We already have a long-term architectural direction in ECP-0004: broadcast identity is the primary convergence key and source identity is a fallback. This change applies that rule to the current discovery boundary instead of waiting for deeper ingest metadata work.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- Channel-to-broadcast identity helper in `ec-core`
|
||||
- Typed lineup metadata extraction in `ec-hdhomerun`
|
||||
- Default `stream_id` synthesis updates in `ec-node`
|
||||
- Unit coverage for identity derivation and lineup parsing
|
||||
- Operator docs describing the precedence rules
|
||||
|
||||
Out of scope:
|
||||
|
||||
- Deep MPEG-TS/PSIP extraction of TSID/program data from live transport packets
|
||||
- Automatic migration of already-running source-scoped publishers
|
||||
- Cryptographic anti-spoofing or admission control for gossip/DHT announcements
|
||||
|
||||
## Constraints
|
||||
|
||||
- Must remain additive and reversible
|
||||
- Must not break explicit operator-provided `--stream-id`
|
||||
- Must keep non-broadcast-aware sources working unchanged
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep source-scoped defaults until full PSIP parsing exists. Rejected because it preserves a known convergence flaw in the current network behavior.
|
||||
- Force all operators to manually specify canonical `--stream-id`. Rejected because it is error-prone and defeats zero-config dedupe.
|
||||
- Collapse purely onto channel number. Rejected because channel number alone is too weak without additional hints.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- Additive change: only synthesized defaults change, not explicit IDs.
|
||||
- Roll back by restoring source-scoped default synthesis.
|
||||
- Existing automation can pin old behavior with explicit `--stream-id` if needed during transition.
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# ECP-0090: PAT-Derived Broadcast Identity Promotion
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Decision
|
||||
|
||||
Extend default discovery identity synthesis to probe early MPEG-TS packets for Program Association Table (PAT) data and use that to strengthen broadcast-scoped stream IDs.
|
||||
|
||||
Current rule:
|
||||
|
||||
- If source/channel metadata already yields a usable broadcast identity, use it.
|
||||
- If transport probing finds PAT data, fill missing `transport_stream_id` and `program_number`.
|
||||
- If PAT shows exactly one non-zero program, that program is eligible for broadcast-scoped convergence.
|
||||
- If PAT is ambiguous (multiple non-zero programs), do not guess; keep `program_number` unset and fall back to source scope when needed.
|
||||
- If the probe sees ATSC PSIP traffic, label the standard as `atsc`; otherwise use a generic `mpegts` fallback when PAT is the only signal.
|
||||
|
||||
## Motivation
|
||||
|
||||
ECP-0089 fixed the first duplication bug by preferring channel metadata over source-local IDs. That still left a hole:
|
||||
|
||||
- raw TS sources without lineup metadata could not converge,
|
||||
- channel metadata could be incomplete or inconsistent,
|
||||
- and weak identity inputs risked accidental convergence on the wrong broadcast.
|
||||
|
||||
PAT-derived identity gives us a stronger on-wire fingerprint without waiting for full PSIP/VCT parsing.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- PAT parsing in `ec-ts`
|
||||
- bounded transport probing for `tsid` / single-program extraction
|
||||
- source integration for raw TS inputs and HDHomeRun enrichment
|
||||
- tests that enforce "single program promotes, ambiguous stream does not"
|
||||
|
||||
Out of scope:
|
||||
|
||||
- full ATSC VCT/MGT descriptor parsing
|
||||
- DVB service table parsing
|
||||
- cryptographic origin authentication for discovery announcements
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep trusting channel metadata only. Rejected because it leaves raw TS paths weak and incomplete.
|
||||
- Always promote from PAT even with multiple programs. Rejected because it guesses the channel and increases junk-collision risk.
|
||||
- Require manual `--stream-id` for TS inputs. Rejected because it pushes canonicalization burden onto operators.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- Additive and reversible: explicit `--stream-id` still overrides everything.
|
||||
- Roll back by removing PAT probing and returning to metadata-only promotion.
|
||||
- Future PSIP/VCT work can refine the broadcast identity without changing the override surface.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# ECP-0091: Full ATSC PSIP Parsing and Real-Sample Validation
|
||||
|
||||
Status: Implemented
|
||||
|
||||
## Decision
|
||||
|
||||
Extend `ec-ts` from PAT-only plus partial PSIP awareness into table-level ATSC PSIP parsing for:
|
||||
|
||||
- `MGT`
|
||||
- `TVCT` / `CVCT`
|
||||
- `STT`
|
||||
- `RRT`
|
||||
- `EIT`
|
||||
- `ETT`
|
||||
|
||||
Additional rules:
|
||||
|
||||
- `EIT` and `ETT` are parsed on their MGT-assigned PIDs, not only on base PID `0x1FFB`.
|
||||
- Raw descriptor bytes are preserved in parsed tables where we do not yet expose typed descriptor
|
||||
structs.
|
||||
- ATSC STT is converted to Unix time using GPS epoch semantics instead of the earlier placeholder
|
||||
arithmetic.
|
||||
- Real-data checks are added as ignored tests using external ATSC captures rather than vendoring
|
||||
large TS fixtures into the repo.
|
||||
|
||||
## Motivation
|
||||
|
||||
ECP-0089 and ECP-0090 fixed the first discovery-identity hole by preferring broadcast metadata and
|
||||
then strengthening it with PAT probing. That still left two gaps:
|
||||
|
||||
- PSIP coverage was incomplete, so the parser could detect “this is ATSC” without understanding the
|
||||
rest of the broadcast metadata surface.
|
||||
- Validation was synthetic-only, which is not enough for transport tables that are notoriously easy
|
||||
to parse incorrectly while still passing fixture tests.
|
||||
|
||||
We need both: fuller PSIP coverage and a rerunnable path against known real captures.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- full table parsing for `MGT`, `TVCT/CVCT`, `STT`, `RRT`, `EIT`, and `ETT`
|
||||
- multiple-string structure parsing with conservative text decoding for common uncompressed modes
|
||||
- non-base PID handling for `EIT` / `ETT`
|
||||
- corrected STT to Unix-time conversion
|
||||
- ignored real-sample tests against `tsduck-test` ATSC captures
|
||||
|
||||
Out of scope:
|
||||
|
||||
- semantic parsing of all ATSC descriptor payloads
|
||||
- DVB/ISDB parity in this change
|
||||
- using `RRT`, `EIT`, or `ETT` directly in the default discovery key
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep partial PSIP parsing and rely on PAT + lineup hints only. Rejected because it leaves too much
|
||||
unverified broadcast metadata on the floor.
|
||||
- Vendor large real TS captures into the repo. Rejected because it bloats the tree and makes review
|
||||
worse.
|
||||
- Parse `EIT` / `ETT` only on `0x1FFB`. Rejected because live ATSC streams carry them on the PIDs
|
||||
advertised by `MGT`.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- Additive for existing callers: explicit `--stream-id` still overrides discovery identity.
|
||||
- If needed, roll back by reverting the new table parsers while keeping PAT-based identity promotion.
|
||||
- The ignored real-sample tests remain opt-in and do not make normal CI depend on external downloads.
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
# ECP-0092: Ethereum rails and private settlement for every.channel
|
||||
|
||||
Status: Draft
|
||||
|
||||
## Problem / context
|
||||
|
||||
`every.channel` already has useful anti-junk and manifest integrity primitives, but they stop at
|
||||
repo-local formats: custom Merkle trees, JSON body hashes, and Ed25519 signatures. That is enough
|
||||
for local validation, not for the broader direction now requested:
|
||||
|
||||
- stream identity, broadcast de-duplication, manifests, and transport announcements should all have
|
||||
Ethereum-compatible representations,
|
||||
- private-chain settlement should reuse existing Ethereum tooling instead of inventing a custom
|
||||
ledger,
|
||||
- iroh should remain the transport substrate,
|
||||
- storage on chain must stay compact, and
|
||||
- `ecp-forge` should become the high-bandwidth head node for sequencing, archival, and bootstrap.
|
||||
|
||||
This supersedes the specific ECP-0022 alternative that rejected “global consensus / blockchain for
|
||||
manifests” as overkill. The new direction is not a public general-purpose chain. It is a private,
|
||||
Ethereum-compatible settlement rail for every.channel commitments.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a dual-plane architecture:
|
||||
|
||||
- `iroh` remains the transport, discovery, and media movement layer.
|
||||
- Local protocol integrity remains first-class:
|
||||
- `manifest_id` stays BLAKE3 over the manifest body,
|
||||
- object membership proofs stay `merkle+blake3`,
|
||||
- Ed25519 manifest signatures remain supported.
|
||||
- Ethereum-compatible commitments become an additive settlement/mirroring layer for stream
|
||||
identity, transport announcements, and manifests.
|
||||
- Ethereum-native signatures are added alongside local signatures:
|
||||
- manifests may carry secp256k1 EIP-712 signatures over `ManifestBody`,
|
||||
- no existing local signature path is removed as part of this proposal.
|
||||
- On-chain state remains compact:
|
||||
- manifests store only stream/epoch ids plus commitment hashes,
|
||||
- control announcements store only stream ids plus announcement commitments,
|
||||
- full media bytes, manifests, PSIP tables, and chunk inventories remain off-chain.
|
||||
- The chain is private and purpose-built for every.channel use cases:
|
||||
- no arbitrary application compute in the near term,
|
||||
- no fraud-proof system in the near term,
|
||||
- permissionless submission as far as operationally possible,
|
||||
- pseudonymous participation preferred over account-heavy identity.
|
||||
|
||||
## Details
|
||||
|
||||
### Canonical Ethereum representations
|
||||
|
||||
Introduce Solidity-compatible struct mirrors for the protocol surfaces we actually ship:
|
||||
|
||||
- `BroadcastId`
|
||||
- `SourceId`
|
||||
- `StreamKey`
|
||||
- `StreamDescriptor`
|
||||
- `StreamControlAnnouncement`
|
||||
- `ChunkId`
|
||||
- `ManifestVariant`
|
||||
- `ManifestBody`
|
||||
- `ManifestSignature`
|
||||
- `Manifest`
|
||||
|
||||
These are encoded via Solidity ABI rules and hashed with `keccak256`. They mirror the shipped
|
||||
protocol types; they do not replace the local wire format or local hashing rules.
|
||||
|
||||
### Manifest commitments
|
||||
|
||||
Publisher-generated manifests may carry a set of Ethereum commitments as first-class fields:
|
||||
|
||||
- `manifest-data-merkle-keccak256-v1`
|
||||
- Keccak Merkle root over ordered chunk hashes, or over ordered variant roots for multi-variant
|
||||
manifests.
|
||||
- `manifest-body-abi-keccak256-v1`
|
||||
- `keccak256(abi.encode(EthManifestBody))`
|
||||
- `manifest-envelope-abi-keccak256-v1`
|
||||
- `keccak256(abi.encode(EthManifest))`
|
||||
|
||||
`manifest_id` and `merkle_root` remain the local BLAKE3-backed protocol fields. Ethereum
|
||||
commitments are explicit, separately named, and never inferred from those local field names alone.
|
||||
|
||||
### Signature rails
|
||||
|
||||
Manifest signatures become dual-rail:
|
||||
|
||||
- local signatures continue to sign the local BLAKE3 `manifest_id` with Ed25519,
|
||||
- Ethereum-native signatures sign the typed `EthManifestBody` with secp256k1 EIP-712,
|
||||
- both signature families may appear on the same manifest.
|
||||
|
||||
The Ethereum signature target is `ManifestBody`, not the full manifest envelope, so signatures do
|
||||
not become self-referential when more signatures are appended.
|
||||
|
||||
### Stream and control commitments
|
||||
|
||||
Stream descriptors and control announcements may also carry Ethereum-compatible commitments:
|
||||
|
||||
- `stream-id-keccak256-v1`
|
||||
- `stream-descriptor-abi-keccak256-v1`
|
||||
- `control-announcement-abi-keccak256-v1`
|
||||
|
||||
This ties broadcast-scoped discovery identity to the same settlement vocabulary used by manifests
|
||||
without forcing transport-time consumers to abandon the local vocabulary they already use.
|
||||
|
||||
### Solidity rail contract
|
||||
|
||||
Add a minimal contract that stores only the latest compact pointers:
|
||||
|
||||
- latest manifest pointer per `stream_id + epoch_id`
|
||||
- latest transport announcement pointer per `stream_id`
|
||||
- event emissions for historical indexing
|
||||
|
||||
The contract must stay event-heavy and storage-light. Full manifests stay in iroh / relay / archive
|
||||
storage.
|
||||
|
||||
### `ecp-forge` role
|
||||
|
||||
`ecp-forge` becomes the maximal head node for this network:
|
||||
|
||||
- private-chain bootstrap and packaging,
|
||||
- archival indexer for settlement events,
|
||||
- high-bandwidth manifest and control announcer,
|
||||
- optional sequencer / execution-client host for the private network.
|
||||
|
||||
This role is operational, not constitutional. Other nodes must still be able to validate, mirror,
|
||||
and submit commitments without going through a closed control plane.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Keep custom BLAKE3-only manifests forever. Rejected because it strands every.channel outside the
|
||||
tooling and audit surface of the Ethereum ecosystem.
|
||||
- Replace BLAKE3 and Ed25519 immediately. Rejected because it weakens the local protocol’s existing
|
||||
integrity path before the private rail is operational and discards useful cryptographic
|
||||
properties for no near-term operational win.
|
||||
- Build a custom blockchain. Rejected because existing Rust + Solidity stacks are already good
|
||||
enough and reduce implementation risk.
|
||||
- Use public Ethereum mainnet / arbitrary public L2s directly. Rejected for now because cost,
|
||||
privacy, and operational control do not fit the current network shape.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
1. Add `ec-eth` with canonical Solidity-compatible representations and Keccak commitments.
|
||||
2. Preserve the local BLAKE3/Ed25519 rail as the default transport integrity path.
|
||||
3. Add optional secp256k1 EIP-712 manifest-body signatures alongside Ed25519 signatures.
|
||||
4. Add the minimal Solidity rail contract for compact settlement pointers.
|
||||
5. Teach publishers/indexers to mirror manifests and announcements onto the private chain.
|
||||
6. Stand up the private Ethereum-compatible network on `ecp-forge` and companion nodes.
|
||||
|
||||
Teardown:
|
||||
|
||||
- back out the Ethereum rail changes explicitly in code; the preserved local rail remains as the
|
||||
fallback integrity path.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Which execution stack should back the private network first: a slim Reth-based node, or a more
|
||||
minimal Rust EVM deployment?
|
||||
- When the private rail becomes live, do we require both signature families for publisher policy, or
|
||||
is one valid rail-specific signature enough for a given transport or settlement action?
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
# ECP-0093: `ecp-forge` OP Stack Sepolia testnet and observation consensus rail
|
||||
|
||||
Status: Draft
|
||||
|
||||
## Problem / context
|
||||
|
||||
`every.channel` now has additive Ethereum-compatible commitments and signatures, but it does not yet
|
||||
have:
|
||||
|
||||
- a concrete chain-operator path for bringing up a real OP Stack testnet on `ecp-forge`,
|
||||
- a repo-owned bootstrap workflow that matches the current official OP Stack deployment guidance,
|
||||
- an application-level consensus surface for "reality-derived" blocks where witnesses attest to the
|
||||
same observed broadcast epoch, or
|
||||
- a reproducible smoke path that pushes real every.channel archive data through the rail.
|
||||
|
||||
The current `EveryChannelRail` contract is useful as a compact pointer store, but it does not
|
||||
capture the consensus shape requested by the founder: nodes independently discover an observation
|
||||
from reality, then witnesses sign off on that shared observation hash.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a two-part rollout:
|
||||
|
||||
1. Stand up an OP Stack Sepolia-anchored testnet on `ecp-forge` using the official OP Stack stack:
|
||||
`op-deployer`, `op-geth`, `op-node`, `op-batcher`, `op-proposer`, `op-challenger`, and
|
||||
optional `op-dispute-mon`.
|
||||
2. Introduce an application-level observation consensus rail for every.channel:
|
||||
- an `ObservationHeader` is derived from real stream epoch data,
|
||||
- witnesses attest to the derived observation hash,
|
||||
- the observation is finalized when quorum is reached,
|
||||
- full media bytes remain off-chain.
|
||||
|
||||
This keeps Ethereum as the ordering/finality substrate while making the application-level "block"
|
||||
come from reality, not from arbitrary contract writes.
|
||||
|
||||
## Details
|
||||
|
||||
### `ecp-forge` OP Stack deployment
|
||||
|
||||
- Use the official OP Stack deployment topology described in the current Optimism chain-operator
|
||||
tutorial:
|
||||
- Sepolia L1 contracts via `op-deployer`,
|
||||
- `op-geth` + `op-node` sequencer,
|
||||
- `op-batcher`,
|
||||
- `op-proposer`,
|
||||
- `op-challenger`,
|
||||
- optional `op-dispute-mon`.
|
||||
- Host these components on `ecp-forge` via repo-owned NixOS services and pinned official container
|
||||
images.
|
||||
- Keep L2 RPC surfaces private by default; expose P2P only as required for testnet participation.
|
||||
- Default L1 RPC / beacon endpoints may use public Sepolia providers, but deployment and operation
|
||||
require a repo-managed Sepolia private key secret with sufficient ETH.
|
||||
- `op-challenger` and `op-dispute-mon` require a repo-managed Cannon absolute prestate artifact.
|
||||
If the prestate is absent, the core rollup may still run, but the dispute path is intentionally
|
||||
gated off instead of starting with incomplete configuration.
|
||||
|
||||
### Observation consensus
|
||||
|
||||
Introduce a new application-level surface alongside `EveryChannelRail`:
|
||||
|
||||
- `ObservationHeader`
|
||||
- `streamHash`
|
||||
- `epochHash`
|
||||
- `parentObservationHash`
|
||||
- `dataRoot`
|
||||
- `locatorHash`
|
||||
- `observedUnixMs`
|
||||
- `sequence`
|
||||
- `ObservationHash`
|
||||
- `keccak256(abi.encode(ObservationHeader))`
|
||||
- `ObservationSlot`
|
||||
- `keccak256(abi.encode(streamHash, epochHash))`
|
||||
|
||||
Witnesses attest to an `ObservationHash`, not directly to arbitrary payloads. This lets the chain
|
||||
finalize the winning observation for a `(stream, epoch)` slot without storing the full manifest,
|
||||
chunk list, PSIP data, or media bytes.
|
||||
|
||||
### Witness model
|
||||
|
||||
For the first testnet tranche:
|
||||
|
||||
- witness membership is registry-backed,
|
||||
- quorum is fixed and explicit,
|
||||
- no staking or slashing is required,
|
||||
- sending an attestation transaction from a witness address is sufficient "sign off".
|
||||
|
||||
Off-chain EIP-712 witness signatures may be added later for relayed submission, but they are not a
|
||||
prerequisite for the first live testnet.
|
||||
|
||||
### Real-data validation
|
||||
|
||||
Use real every.channel archive data already present on `ecp-forge` as the smoke source:
|
||||
|
||||
- derive observation headers from actual archive JSONL entries,
|
||||
- submit them to a local Anvil deployment of the observation ledger,
|
||||
- prove quorum/finalization against production-shaped data before attempting Sepolia rollout.
|
||||
|
||||
## Components
|
||||
|
||||
- `contracts/EveryChannelRail.sol`
|
||||
- compact manifest / transport pointer store (existing rail)
|
||||
- `contracts/EveryChannelWitnessRegistry.sol`
|
||||
- witness membership for observation consensus
|
||||
- `contracts/EveryChannelObservationLedger.sol`
|
||||
- candidate observation storage, attestation tracking, and finalization
|
||||
- `scripts/op-stack/download-op-deployer.sh`
|
||||
- pinned `op-deployer` fetch helper
|
||||
- `scripts/op-stack/setup-rollup.sh`
|
||||
- repo-owned OP Stack bootstrap/config generation
|
||||
- `scripts/op-stack/anvil-reality-smoke.sh`
|
||||
- local smoke against Anvil using real `ecp-forge` archive data
|
||||
- `nix/modules/ec-op-stack.nix`
|
||||
- NixOS service/module surface for `ecp-forge`
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Use Base Sepolia directly as the only test environment. Rejected because it validates contract
|
||||
compatibility, but not operation of an every.channel-owned chain.
|
||||
- Wait for a future custom consensus implementation before any chain bring-up. Rejected because it
|
||||
delays operator learning and hides infrastructure problems until late.
|
||||
- Put media bytes or full manifests on chain. Rejected because it is operationally wasteful and not
|
||||
required for observation finalization.
|
||||
|
||||
## Rollout / teardown plan
|
||||
|
||||
1. Add the observation-ledger contracts and Foundry tests.
|
||||
2. Add Anvil + real-archive smoke scripts.
|
||||
3. Add an `ec-op-stack` Nix module and `ecp-forge` integration points.
|
||||
4. Wire secrets for a Sepolia deployment key on `ecp-forge`.
|
||||
5. Bring up the OP Stack services on `ecp-forge`.
|
||||
6. Deploy observation-ledger contracts onto the new chain.
|
||||
7. Submit real archive-derived observations and verify finalization.
|
||||
|
||||
Teardown:
|
||||
|
||||
- disable `services.every-channel.op-stack`,
|
||||
- stop and remove the OP Stack containers/state directories,
|
||||
- preserve the local Anvil smoke path and the additive Ethereum rail code.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should `ecp-forge` be the only sequencer for the first tranche, or should a second verifier-only
|
||||
peer be provisioned immediately?
|
||||
- When we move beyond a registry-backed witness set, do we want bonds, reputation, or both?
|
||||
- Should finalized observation slots bridge back into the existing manifest/transport pointer
|
||||
contract automatically, or remain a separate ledger surface for the first rollout?
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# ECP-0094: NBC Browser-Backed Source with Adobe Auth
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Add `nbc.com/watch/...` as a manual source in the Tauri app by treating it as a browser-backed
|
||||
capture source instead of a direct manifest source.
|
||||
|
||||
The desktop app will:
|
||||
|
||||
- recognize NBC watch URLs as a distinct `nbc` source kind
|
||||
- launch Chrome with a persistent profile so Adobe Pass / MVPD state can survive across runs
|
||||
- wait for the Adobe/NBC entitlement sequence seen in `intake/`
|
||||
- capture the rendered video element from the authenticated browser tab and feed those frames into
|
||||
the existing ffmpeg CMAF ladder path
|
||||
|
||||
## Motivation
|
||||
|
||||
The intake traces show NBC live playback is not exposed as a plain live HLS or clear DASH input:
|
||||
|
||||
- page metadata comes from `friendship.nbc.com`
|
||||
- entitlement is gated by Adobe Pass on `sp.auth.adobe.com`
|
||||
- authorization is validated through NBC's `tokenverifier`
|
||||
- playout resolves to MediaTailor DASH plus Widevine license requests
|
||||
|
||||
That means the existing source model cannot ingest NBC by simply resolving a URL for ffmpeg. We
|
||||
need a source path that can execute the browser login/auth/player flow first.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- `nbc.com/watch/...` detection in the Tauri add-stream flow
|
||||
- persistent manual-source storage for the new kind
|
||||
- Chrome launch/profile handling for NBC playback
|
||||
- readiness checks tied to the Adobe/session/authorize/token-verifier path observed in the traces
|
||||
- browser-frame capture into the existing live transcode/publish pipeline
|
||||
|
||||
Out of scope:
|
||||
|
||||
- generic DRM extraction or CDM key handling
|
||||
- unattended credential entry for every MVPD provider
|
||||
- CLI ingest support in `ec-node`
|
||||
- perfect parity with native audio capture in the first cut
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Treat NBC as a normal HLS/DASH source. Rejected because the trace shows DRM-gated DASH that
|
||||
ffmpeg cannot ingest directly.
|
||||
- Re-implement the full Adobe + MVPD HTTP flow in-process. Rejected for the first cut because the
|
||||
real browser/player state is already required for playout and provider flows vary.
|
||||
- Leave NBC unsupported. Rejected because the traces are sufficient to define a pragmatic desktop
|
||||
path now.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The change is additive and isolated to the desktop app's manual-source flow.
|
||||
- Roll back by removing the `nbc` source kind and browser capture path.
|
||||
- Existing HDHomeRun, HLS, Linux DVB, and yt-dlp flows remain unchanged.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# ECP-0095: Native macOS Webview First for NBC Auth and Playback
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
On macOS, the Tauri app should attempt NBC playback through an in-app `WebviewWindow` backed by
|
||||
`WKWebView` before launching an external browser.
|
||||
|
||||
The native path will:
|
||||
|
||||
- create a dedicated NBC auth/player window inside the app with a persistent webview data
|
||||
directory
|
||||
- keep Adobe Pass / MVPD interaction inside the native app window, including popup sign-in flows
|
||||
- use native `WKWebView` JavaScript evaluation and snapshot APIs to drive readiness checks and
|
||||
capture rendered frames for ffmpeg
|
||||
|
||||
The existing Chrome-backed path remains as a fallback when native playback cannot become ready.
|
||||
|
||||
## Motivation
|
||||
|
||||
The first NBC implementation proved that browser-backed capture is the right source model, but it
|
||||
still pushes authentication into an external Chrome session. For the desktop app, that is a worse
|
||||
operator experience than a native in-app window and makes session management less coherent.
|
||||
|
||||
Tauri 2 already exposes the platform webview and, on macOS, gives access to the underlying
|
||||
`WKWebView`. That is enough to move the login and capture loop in-process without redesigning the
|
||||
rest of the CMAF ladder or MoQ publish flow.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- macOS-only `WKWebView` session creation for NBC sources
|
||||
- popup/new-window handling for MVPD login flows
|
||||
- native JavaScript probing plus native snapshot capture for the existing frame pipeline
|
||||
- automatic fallback to the existing Chrome path when native playback fails to get ready
|
||||
|
||||
Out of scope:
|
||||
|
||||
- removing the Chrome fallback entirely
|
||||
- guaranteeing DRM parity with Chrome across every NBC playback variant
|
||||
- audio capture changes beyond the current video-first path
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep the external Chrome-only path. Rejected because the native app can host the auth/player
|
||||
surface directly on macOS.
|
||||
- Replace the entire source implementation with custom CEF/Chromium embedding. Rejected because it
|
||||
is materially heavier than using Tauri's existing native webview APIs.
|
||||
- Use an in-app webview only for login, then continue playback in Chrome. Rejected for the first
|
||||
pass because it still splits operator state across two browser stacks.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The change is additive and scoped to NBC handling in the Tauri app on macOS.
|
||||
- If native playback proves unreliable for a given stream/auth path, the app falls back to the
|
||||
existing Chrome implementation automatically.
|
||||
- Roll back by removing the native webview path and retaining the Chrome-backed implementation from
|
||||
ECP-0094.
|
||||
53
evolution/proposals/ECP-0096-public-nbc-guide-before-auth.md
Normal file
53
evolution/proposals/ECP-0096-public-nbc-guide-before-auth.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# ECP-0096: Public NBC Guide Before Auth
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Add a public NBC guide feed to the Tauri app so browseable NBC linear channels appear before any
|
||||
Adobe or MVPD authentication happens.
|
||||
|
||||
The desktop app will:
|
||||
|
||||
- fetch the unauthenticated `friendship.nbc.com` live guide shelf seen in `intake/`
|
||||
- materialize those rows as normal `StreamDescriptor` entries in the existing channel list
|
||||
- route each discovered row to a stable `nbc.com/live?brand=...` URL so selecting a card enters
|
||||
the existing NBC auth and playback path only when the user chooses to watch
|
||||
|
||||
## Motivation
|
||||
|
||||
The NBC capture already proved that playback is auth-gated, but the same trace also contains a
|
||||
public guide layer with channel titles, current programs, branding, and stream access names. That
|
||||
is enough to support browse-before-auth behavior without exposing entitlement or playout tokens.
|
||||
|
||||
This keeps discovery public while preserving the existing browser-backed auth boundary from
|
||||
ECP-0094 and ECP-0095.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- unauthenticated NBC public guide fetches for channel browsing
|
||||
- conversion of live guide rows into `nbc` stream descriptors and sources
|
||||
- Tauri UI updates needed to surface `nbc` rows in the channel list
|
||||
|
||||
Out of scope:
|
||||
|
||||
- reverse-engineering or exposing entitlement responses
|
||||
- direct manifest playback without the existing auth flow
|
||||
- a full public sports-event catalog beyond the live channel guide in this cut
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep NBC URL-only. Rejected because it hides browseable public metadata that the trace already
|
||||
exposes.
|
||||
- Expose MediaTailor or token-verifier outputs in discovery. Rejected because those belong to the
|
||||
authenticated playback path.
|
||||
- Build a separate NBC-only discovery UI. Rejected because the existing channel list can host the
|
||||
guide with less surface area.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The change is additive to the desktop app and only affects discovery presentation.
|
||||
- Roll back by removing the public guide fetch and the synthesized NBC discovery entries.
|
||||
- Existing NBC auth and playback handling remains the same once a user chooses to watch.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# ECP-0097: Tauri Desktop Bundle Boot via Relative Assets and Custom Protocol
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Make the Tauri desktop frontend boot from bundled assets reliably by:
|
||||
|
||||
- building the Tauri UI with relative Trunk asset URLs
|
||||
- exposing the standard `custom-protocol` cargo feature so direct Cargo runs can load bundled
|
||||
assets instead of the dev server URL
|
||||
- showing a minimal boot-status overlay until the Dioxus app mounts, so startup failures are no
|
||||
longer silent blank windows
|
||||
|
||||
## Motivation
|
||||
|
||||
The desktop shell was starting as a blank window even though the frontend bundle itself rendered
|
||||
correctly over HTTP. Two separate issues caused that failure mode:
|
||||
|
||||
- the Trunk bundle used root-absolute asset paths, which are fragile for desktop asset loading
|
||||
- `cargo run` without the Tauri custom-protocol feature followed the dev-server path instead of the
|
||||
bundled `dist/` assets
|
||||
|
||||
This made local desktop verification ambiguous and hid useful error information from operators.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- Tauri UI bundling for desktop
|
||||
- direct Cargo run behavior for the desktop app
|
||||
- a minimal visible frontend boot diagnostic
|
||||
|
||||
Out of scope:
|
||||
|
||||
- redesigning the viewer UI
|
||||
- changing the web deployment path for non-Tauri surfaces
|
||||
- full developer tooling around webview devtools
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep root-absolute Trunk paths and require `cargo tauri dev` only. Rejected because desktop
|
||||
bundle verification remains fragile and blank-window failures remain opaque.
|
||||
- Add only the boot overlay. Rejected because diagnostics help, but the bundle/load path still
|
||||
needs correction.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The change is local to the Tauri desktop app.
|
||||
- Roll back by restoring the prior Trunk public URL and removing the package feature / boot
|
||||
overlay.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# ECP-0098: NBC Native Auth Completion and Explicit Guide Program Labels
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Tighten the macOS-native NBC flow so Adobe Pass background-login completion in popup windows is
|
||||
treated as a successful auth handoff, and expose explicit current-program metadata for NBC public
|
||||
guide rows so the desktop channel list can render live program info directly.
|
||||
|
||||
The desktop app will:
|
||||
|
||||
- treat Adobe's `completeBackgroundLogin.html` popup landing as a native auth completion signal
|
||||
- refocus and reload the main NBC player webview once that signal arrives so playback readiness can
|
||||
continue in the same in-app session
|
||||
- reload the main NBC player webview once when it is already back on a fully loaded NBC watch
|
||||
surface but still has not materialized a video element
|
||||
- persist the current live program as first-class guide metadata instead of relying only on the
|
||||
generic `number` field
|
||||
|
||||
## Motivation
|
||||
|
||||
The first native `WKWebView` pass gets the user into Verizon login, but the Adobe handoff can end
|
||||
inside a short-lived popup instead of automatically returning control to the main player window.
|
||||
Without explicit completion handling, the native flow appears to stall after successful login.
|
||||
|
||||
Separately, NBC public discovery already carries current-program titles, but the desktop list was
|
||||
rendering only the generic `number` slot. That makes guide rows look incomplete even when the
|
||||
metadata is already available.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- native popup completion handling for Adobe background login on macOS
|
||||
- readiness-loop updates needed to continue playback after popup completion or an already-authenticated return to the main watch surface
|
||||
- explicit current-program metadata and guide-row subtitle rendering for NBC discovery rows
|
||||
|
||||
Out of scope:
|
||||
|
||||
- a broader redesign of the channel list for all source types
|
||||
- removal of the Chrome NBC fallback path
|
||||
- changes to NBC entitlement semantics beyond surfacing existing discovery metadata
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep waiting on the main player page only. Rejected because Adobe can finish inside a popup and
|
||||
leave the main page idle.
|
||||
- Continue storing NBC program info only in `StreamDescriptor.number`. Rejected because it obscures
|
||||
intent and makes guide rendering brittle.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The auth-completion change is additive to the native macOS NBC path and falls back to Chrome if
|
||||
playback still does not become ready.
|
||||
- The guide-label change is additive metadata plus UI presentation.
|
||||
- Roll back by removing the popup completion hook and the explicit program-label rendering.
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# ECP-0099: NBC Hidden Native Worker Mode Instead of True Headless Chrome
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Do not target true Chrome headless mode for NBC playback in `every.channel`.
|
||||
|
||||
Instead, add a macOS hidden native-worker mode for the existing `WKWebView` path so NBC playback
|
||||
can run without a visible browser window when the session is already authenticated.
|
||||
|
||||
The desktop app will:
|
||||
|
||||
- allow the native NBC webview windows to start hidden behind `EVERY_CHANNEL_NBC_HIDE_WINDOWS=1`
|
||||
- surface those hidden windows only when the navigation flow reaches an interactive auth page
|
||||
- refuse the visible Chrome fallback when hidden native mode is explicitly requested
|
||||
|
||||
## Motivation
|
||||
|
||||
On this Mac, local Google Chrome in true headless mode does not expose EME / `requestMediaKeySystemAccess`,
|
||||
which makes Widevine-backed NBC playback non-viable in a real headless browser process.
|
||||
|
||||
That means the practical path to future unattended playback is not “headless Chrome,” but an
|
||||
invisible native browser surface with a warm authenticated session and explicit surfacing only
|
||||
when auth has expired.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- macOS hidden-window behavior for the existing NBC `WKWebView` path
|
||||
- auth-page detection that reveals the hidden window when user interaction is required
|
||||
- blocking visible Chrome fallback when hidden mode is requested
|
||||
|
||||
Out of scope:
|
||||
|
||||
- automating Verizon credential entry in this cut
|
||||
- making Chrome headless support Widevine / EME
|
||||
- guaranteeing unattended runs when the MVPD session has expired
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Use true Chrome headless. Rejected because local testing showed no EME path.
|
||||
- Keep only the visible native/Chrome flows. Rejected because a warm session should be able to run
|
||||
without putting browser chrome on screen.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The change is opt-in behind `EVERY_CHANNEL_NBC_HIDE_WINDOWS=1`.
|
||||
- Roll back by removing the hidden-window flag and retaining the visible native flow from ECP-0098.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# ECP-0100: NBC Auth Bootstrap Command for Hidden Worker Sessions
|
||||
|
||||
Status: Proposed
|
||||
|
||||
## Decision
|
||||
|
||||
Add an explicit `bootstrap_nbc_auth` command to the Tauri app so operators can warm an NBC /
|
||||
Adobe session ahead of hidden playback runs.
|
||||
|
||||
The command will:
|
||||
|
||||
- accept either an existing NBC stream id or a raw `nbc.com/watch/...` or `nbc.com/live?...` URL
|
||||
- run the macOS native NBC path in hidden mode by default
|
||||
- surface the auth window only when MVPD interaction is actually required
|
||||
- close the bootstrap window after the session is ready so later hidden playback can reuse the
|
||||
persisted session state
|
||||
|
||||
## Motivation
|
||||
|
||||
Hidden worker mode makes unattended playback plausible only when the session is already warm.
|
||||
Without an explicit bootstrap surface, the operator has to guess which playback attempt should be
|
||||
used to re-authenticate the session.
|
||||
|
||||
An explicit bootstrap command gives `every.channel` a clean operator primitive: warm auth now,
|
||||
then let later hidden playback reuse that session without additional visible browser chrome.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- a Tauri backend command for NBC auth bootstrap
|
||||
- a small operator affordance in the desktop UI to invoke that command
|
||||
- reusing the same persisted native webview data directory as hidden playback
|
||||
|
||||
Out of scope:
|
||||
|
||||
- a full standalone CLI lifecycle for bootstrap without the Tauri app runtime
|
||||
- storing Verizon credentials in the repo
|
||||
- automating expired-auth recovery beyond surfacing the interactive window when needed
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Reuse ordinary playback attempts as implicit bootstrap. Rejected because it obscures operator
|
||||
intent and complicates hidden-run automation.
|
||||
- Build a separate bootstrap-only app. Rejected because the existing Tauri runtime already hosts
|
||||
the relevant webview/session state.
|
||||
|
||||
## Rollout / Reversibility
|
||||
|
||||
- The command is additive.
|
||||
- Roll back by removing the command and UI affordance while retaining ECP-0099 hidden native mode.
|
||||
59
flake.nix
59
flake.nix
|
|
@ -13,6 +13,10 @@
|
|||
nixosModules = rec {
|
||||
ec-node = import ./nix/modules/ec-node.nix;
|
||||
ec-runner = import ./nix/modules/ec-runner.nix;
|
||||
ec-netboot = import ./nix/modules/ec-netboot.nix;
|
||||
ec-ipxe-qemu = import ./nix/modules/ec-ipxe-qemu.nix;
|
||||
ec-op-stack = import ./nix/modules/ec-op-stack.nix;
|
||||
ec-publisher-guest = import ./nix/modules/ec-publisher-guest.nix;
|
||||
default = ec-node;
|
||||
};
|
||||
in
|
||||
|
|
@ -29,11 +33,35 @@
|
|||
./nix/nixos/ec-runner.nix
|
||||
] ++ extraModules;
|
||||
};
|
||||
mkPublisher = system: extraModules:
|
||||
nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit self; };
|
||||
modules = [
|
||||
./nix/nixos/ec-runner.nix
|
||||
self.nixosModules.ec-publisher-guest
|
||||
] ++ extraModules;
|
||||
};
|
||||
in
|
||||
{
|
||||
# Base runner system (for normal installs).
|
||||
ec-runner-aarch64 = mkRunner "aarch64-linux" [ ];
|
||||
ec-runner-x86_64 = mkRunner "x86_64-linux" [ ];
|
||||
ec-publisher-x86_64 = mkPublisher "x86_64-linux" [ ];
|
||||
|
||||
# Sovereign forge host (git.every.channel) managed from every.channel.
|
||||
ecp-forge = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = { inherit self; };
|
||||
modules = [
|
||||
agenix.nixosModules.default
|
||||
self.nixosModules.ec-node
|
||||
self.nixosModules.ec-netboot
|
||||
self.nixosModules.ec-ipxe-qemu
|
||||
self.nixosModules.ec-op-stack
|
||||
./nix/nixos/ecp-forge.nix
|
||||
];
|
||||
};
|
||||
|
||||
# Netboot artifacts (iPXE/PXE).
|
||||
ec-runner-aarch64-netboot = mkRunner "aarch64-linux" [
|
||||
|
|
@ -86,6 +114,31 @@
|
|||
})
|
||||
];
|
||||
|
||||
ec-publisher-x86_64-netboot = mkPublisher "x86_64-linux" [
|
||||
({ modulesPath, ... }: {
|
||||
imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ];
|
||||
})
|
||||
({ ... }: {
|
||||
services.every-channel.runner.overlayRoot.enable = false;
|
||||
})
|
||||
({ config, pkgs, ... }: {
|
||||
system.build.netboot = pkgs.linkFarm "ec-publisher-netboot" [
|
||||
{
|
||||
name = "kernel";
|
||||
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
|
||||
}
|
||||
{
|
||||
name = "initrd";
|
||||
path = "${config.system.build.netbootRamdisk}/initrd";
|
||||
}
|
||||
{
|
||||
name = "netboot.ipxe";
|
||||
path = "${config.system.build.netbootIpxeScript}/netboot.ipxe";
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
|
||||
# Installer ISO (primarily for x86_64 bring-up).
|
||||
ec-runner-x86_64-iso = mkRunner "x86_64-linux" [
|
||||
({ modulesPath, ... }: {
|
||||
|
|
@ -138,6 +191,8 @@
|
|||
packages = {
|
||||
agenix = agenixPkg;
|
||||
fj = pkgs.forgejo-cli;
|
||||
foundry = pkgs.foundry;
|
||||
solc = pkgs.solc;
|
||||
ec-node = pkgs.callPackage ./nix/pkgs/ec-node.nix { };
|
||||
ec-cli = pkgs.callPackage ./nix/pkgs/ec-cli.nix { };
|
||||
};
|
||||
|
|
@ -157,6 +212,8 @@
|
|||
nodePackages.wrangler
|
||||
agenixPkg
|
||||
forgejo-cli
|
||||
foundry
|
||||
solc
|
||||
uv
|
||||
git
|
||||
just
|
||||
|
|
@ -188,6 +245,8 @@
|
|||
ffmpeg
|
||||
agenixPkg
|
||||
forgejo-cli
|
||||
foundry
|
||||
solc
|
||||
git
|
||||
];
|
||||
|
||||
|
|
|
|||
6
foundry.toml
Normal file
6
foundry.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[profile.default]
|
||||
src = "contracts"
|
||||
test = "contracts/test"
|
||||
script = "contracts/script"
|
||||
out = "out"
|
||||
libs = []
|
||||
BIN
intake/Phillies.proxymansessionv2
Normal file
BIN
intake/Phillies.proxymansessionv2
Normal file
Binary file not shown.
394
intake/soup.csv
Normal file
394
intake/soup.csv
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
Id,URL,Host,Path,Status,Status Code,Method,Content Type,Client Name,Client Address,Remote Address,Request Start Time,Request End Time,Request Duration (ms),Response Start Time,Response End Time,Response Duration (ms),Duration (ms),Time Complete,Request Body Size (bytes),Response Body Size (bytes),Compressed Request Size (bytes),Compressed Response Size (bytes),Comment,Server IP Address,Server Certificate - Common Name,Server Certificate - DNSs,GraphQL Query Name
|
||||
3121,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3105.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3105.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:43.410,16:23:43.410,0,16:23:43.560,16:23:43.588,27,178,16:23:43.588,0,3756303,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3120,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3105.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3105.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55238,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:43.359,16:23:43.359,0,16:23:43.467,16:23:43.615,148,256,16:23:43.615,0,64411,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3116,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:42.931,16:23:42.931,0,16:23:43.019,16:23:43.019,0,88,16:23:43.019,0,279613,0,9714,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3113,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=10&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=8&ns_st_sp=1&ns_st_sc=2&ns_st_psq=3&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=2&ns_st_spc=2&ns_st_cn=1&ns_st_ev=hb&ns_st_po=20004&ns_st_cl=0&ns_st_hc=2&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=20004&ns_st_dpt=20004&ns_st_ipt=10002&ns_st_ap=20004&ns_st_dap=20004&ns_st_et=20004&ns_st_det=20004&ns_st_upc=20004&ns_st_dupc=12047&ns_st_iupc=10002&ns_st_upa=20004&ns_st_dupa=20004&ns_st_iupa=10002&ns_st_lpc=20004&ns_st_dlpc=12047&ns_st_lpa=20004&ns_st_dlpa=20004&ns_st_pa=32203&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999420774&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=0&ns_st_dpc=0&ns_st_pp=2&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55199,sb.scorecardresearch.com - 99.84.215.5:443,16:23:40.776,16:23:40.776,0,16:23:40.917,16:23:40.917,0,141,16:23:40.917,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3112,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,Completed,200,GET,application/json,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:39.402,16:23:39.402,0,16:23:39.453,16:23:39.453,0,50,16:23:39.453,0,28178,0,4171,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3111,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3104.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3104.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55238,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:39.281,16:23:39.281,0,16:23:39.385,16:23:39.397,12,116,16:23:39.397,0,3900497,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3110,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3104.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3104.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:39.129,16:23:39.129,0,16:23:39.331,16:23:39.350,19,222,16:23:39.350,0,64388,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3109,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:38.930,16:23:38.930,0,16:23:39.028,16:23:39.028,0,98,16:23:39.028,0,279613,0,9711,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3108,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3103.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3103.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:36.048,16:23:36.048,0,16:23:36.124,16:23:36.124,0,76,16:23:36.124,0,64262,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3107,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3103.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3103.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:35.171,16:23:35.172,0,16:23:35.197,16:23:35.272,75,101,16:23:35.272,0,3942471,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3106,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:34.931,16:23:34.931,0,16:23:35.056,16:23:35.056,0,125,16:23:35.056,0,279613,0,9715,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3103,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3102.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3102.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:32.010,16:23:32.010,0,16:23:32.055,16:23:32.166,111,155,16:23:32.166,0,3523828,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3102,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3102.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3102.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55238,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:31.997,16:23:31.998,0,16:23:32.020,16:23:32.042,22,44,16:23:32.042,0,64609,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3099,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,Completed,200,GET,application/json,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:31.399,16:23:31.399,0,16:23:31.453,16:23:31.453,0,54,16:23:31.453,0,28178,0,4169,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3098,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:30.931,16:23:30.931,0,16:23:31.017,16:23:31.017,0,87,16:23:31.017,0,279613,0,9715,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3097,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=9&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=8&ns_st_sp=1&ns_st_sc=2&ns_st_psq=3&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=2&ns_st_spc=2&ns_st_cn=1&ns_st_ev=hb&ns_st_po=10002&ns_st_cl=0&ns_st_hc=1&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=10002&ns_st_dpt=10002&ns_st_ipt=10002&ns_st_ap=10002&ns_st_dap=10002&ns_st_et=10002&ns_st_det=10002&ns_st_upc=10002&ns_st_dupc=2045&ns_st_iupc=2045&ns_st_upa=10002&ns_st_dupa=10002&ns_st_iupa=10002&ns_st_lpc=10002&ns_st_dlpc=2045&ns_st_lpa=10002&ns_st_dlpa=10002&ns_st_pa=22201&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999410772&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=0&ns_st_dpc=0&ns_st_pp=2&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55199,sb.scorecardresearch.com - 99.84.215.5:443,16:23:30.774,16:23:30.775,1,16:23:30.861,16:23:30.861,0,87,16:23:30.861,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3096,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:55203,map.mp.nbc.com - 151.101.130.49:443,16:23:30.111,16:23:30.111,0,16:23:30.147,16:23:30.147,0,36,16:23:30.147,4986,42,0,62,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3095,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3101.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-07item_Segment-3101.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55241,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:27.882,16:23:27.936,53,16:23:27.965,16:23:27.988,24,106,16:23:27.988,0,64420,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3094,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3101.mp4,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/1774987260757item-06item_Segment-3101.mp4,Completed,200,GET,video/mp4,Google Chrome,127.0.0.1:55238,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:27.798,16:23:27.843,45,16:23:27.863,16:23:27.999,136,201,16:23:27.999,0,3627642,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,*.pcdn03.cssott.com,*.pcdn03.cssott.com; *.stream.peacocktv.com; *.cdn.peacocktv.com; g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,
|
||||
3093,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:26.931,16:23:26.931,0,16:23:27.011,16:23:27.011,0,80,16:23:27.011,0,279607,0,9715,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3091,https://g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com,,Completed,200,CONNECT,,Google Chrome,127.0.0.1:55228,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,16:23:23.733,16:23:23.753,21,16:23:23.753,16:23:23.753,0,21,16:23:23.753,0,0,0,0,,g001-sle-us-cmaf-prd-cf.pcdn03.cssott.com - 2600:9000:2199:3600:f:ca39:f7c0:93a1:443,,,
|
||||
3090,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/tracking/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/02e128cb-1f94-42eb-9db4-a832507d00ce,Completed,200,GET,application/json,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:23.398,16:23:23.398,0,16:23:23.519,16:23:23.519,0,121,16:23:23.519,0,28178,0,4168,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3089,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?audio=all&subtitle=all&forcedNarrative=true&aws.sessionId=02e128cb-1f94-42eb-9db4-a832507d00ce,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,/v1/dash/7f34bf1814de6fddce84b1e6c296b7a70243b88f/oneapp-atp-dash-sle-4s-generic/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,GET,application/dash+xml,Google Chrome,127.0.0.1:55223,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:23.149,16:23:23.242,93,16:23:23.395,16:23:23.395,0,246,16:23:23.395,0,279589,0,9711,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,mediatailor.us-west-2.amazonaws.com,mediatailor.us-west-2.amazonaws.com; *.mediatailor.us-west-2.amazonaws.com; 69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,
|
||||
3087,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55202,map.mp.nbc.com - 151.101.130.49:443,16:23:20.791,16:23:20.915,124,16:23:20.949,16:23:20.949,0,158,16:23:20.949,1182,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3086,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55203,map.mp.nbc.com - 151.101.130.49:443,16:23:20.791,16:23:20.924,132,16:23:20.967,16:23:20.967,0,176,16:23:20.967,1234,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3085,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55200,map.mp.nbc.com - 151.101.130.49:443,16:23:20.791,16:23:20.924,133,16:23:20.958,16:23:20.958,0,167,16:23:20.958,2250,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3084,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55201,map.mp.nbc.com - 151.101.130.49:443,16:23:20.791,16:23:20.924,133,16:23:20.956,16:23:20.956,0,165,16:23:20.956,2268,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3083,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=8&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=7&ns_st_sp=1&ns_st_sc=2&ns_st_psq=3&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=2&ns_st_spc=2&ns_st_cn=1&ns_st_ev=play&ns_st_po=0&ns_st_cl=0&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=0&ns_st_dpt=0&ns_st_ipt=0&ns_st_ap=0&ns_st_dap=0&ns_st_et=0&ns_st_det=0&ns_st_upc=7957&ns_st_dupc=0&ns_st_iupc=0&ns_st_upa=0&ns_st_dupa=0&ns_st_iupa=0&ns_st_lpc=7957&ns_st_dlpc=0&ns_st_lpa=0&ns_st_dlpa=0&ns_st_pa=12199&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999400770&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=0&ns_st_dpc=0&ns_st_pp=2&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55199,sb.scorecardresearch.com - 99.84.215.5:443,16:23:20.791,16:23:20.906,115,16:23:20.941,16:23:20.942,0,151,16:23:20.942,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3082,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=262144&cn=videoView&et=i&uxnw=169843&uxss=vg2400781&uxct=4&init=1&vcid2=874355218828044267,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55196,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,16:23:20.791,16:23:20.968,177,16:23:21.015,16:23:21.015,0,225,16:23:21.015,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3081,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=7&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=6&ns_st_sp=1&ns_st_sc=1&ns_st_psq=2&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=2&ns_st_ev=end&ns_st_po=4242&ns_st_cl=4238&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=1&ns_st_ad=1&ns_st_ci=12014223&ns_st_pt=4242&ns_st_dpt=0&ns_st_ipt=0&ns_st_ap=4242&ns_st_dap=0&ns_st_et=4244&ns_st_det=2&ns_st_upc=4242&ns_st_dupc=0&ns_st_iupc=0&ns_st_upa=4242&ns_st_dupa=0&ns_st_iupa=0&ns_st_lpc=4242&ns_st_dlpc=0&ns_st_lpa=4242&ns_st_dlpa=0&ns_st_pa=12199&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999400770&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=1&ns_st_dpc=0&ns_st_pp=2&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_an=1&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=va12&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&ns_st_amg=92830326&ns_st_ami=214161967&ns_st_amp=92845689&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=&cs_ad_tu=https%3A%2F%2Fsb.scorecardresearch.com%2Fp%3Fc1%3D3%26c2%3D28881558%26c3%3D92830326%26c4%3D214161967%26c5%3D92845689%26c12%3D%26ns_ad_vevent%3Dv_start%26ns_ad_pcd%3D4%26ns__t%3D1801516585%26ns__p%3D1801516585%26ns_st_pr%3DNBCS%3A%20CSNPhilly%3A%20Baseball%3A%20Live%3A%20MLB%3A%20Men%26ns_st_ge%3D%26ns_st_pu%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Web%3A%20Live%20Event%26ns_st_ep%3DNBCS%3A%20Live%3A%20CSNPhilly%3A%20Baseball%3A%20MLB%3A%20Non-Broadcast%3A%20Philadelphia%20Phillies%3A%20Washington%20Nationals%3A%20nbc_dtc_12014222%5E%26ns_st_ct%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Computer%3A%20Web%3A%20Live%20Event%26cs_vp_sv%3D1%26rn%3D1801516585%26ccr%3D1%26ccrsdk%3D1%26c6%3Dmidroll%26ns_ap_device%3D%26ns_ap_pn%3D,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55198,sb.scorecardresearch.com - 99.84.215.5:443,16:23:20.791,16:23:20.901,111,16:23:20.936,16:23:20.936,0,145,16:23:20.936,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3080,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=262144&cn=slotEnd&et=i&tpos=0&async=0&init=1&slid=0,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55197,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:20.790,16:23:20.967,177,16:23:21.011,16:23:21.011,0,221,16:23:21.011,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3087,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=6&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=5&ns_st_sp=1&ns_st_sc=1&ns_st_psq=2&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=2&ns_st_ev=pause&ns_st_po=4242&ns_st_cl=4238&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=1&ns_st_ad=1&ns_st_ci=12014223&ns_st_pt=4242&ns_st_dpt=4242&ns_st_ipt=4242&ns_st_ap=4242&ns_st_dap=4242&ns_st_et=4242&ns_st_det=4242&ns_st_upc=4242&ns_st_dupc=4242&ns_st_iupc=4242&ns_st_upa=4242&ns_st_dupa=4242&ns_st_iupa=4242&ns_st_lpc=4242&ns_st_dlpc=4242&ns_st_lpa=4242&ns_st_dlpa=4242&ns_st_pa=12199&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999400768&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=1&ns_st_dpc=1&ns_st_pp=2&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_an=1&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=va12&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&ns_st_amg=92830326&ns_st_ami=214161967&ns_st_amp=92845689&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=&cs_ad_tu=https%3A%2F%2Fsb.scorecardresearch.com%2Fp%3Fc1%3D3%26c2%3D28881558%26c3%3D92830326%26c4%3D214161967%26c5%3D92845689%26c12%3D%26ns_ad_vevent%3Dv_start%26ns_ad_pcd%3D4%26ns__t%3D1801516585%26ns__p%3D1801516585%26ns_st_pr%3DNBCS%3A%20CSNPhilly%3A%20Baseball%3A%20Live%3A%20MLB%3A%20Men%26ns_st_ge%3D%26ns_st_pu%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Web%3A%20Live%20Event%26ns_st_ep%3DNBCS%3A%20Live%3A%20CSNPhilly%3A%20Baseball%3A%20MLB%3A%20Non-Broadcast%3A%20Philadelphia%20Phillies%3A%20Washington%20Nationals%3A%20nbc_dtc_12014222%5E%26ns_st_ct%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Computer%3A%20Web%3A%20Live%20Event%26cs_vp_sv%3D1%26rn%3D1801516585%26ccr%3D1%26ccrsdk%3D1%26c6%3Dmidroll%26ns_ap_device%3D%26ns_ap_pn%3D,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55192,sb.scorecardresearch.com - 99.84.215.5:443,16:23:20.782,16:23:20.903,121,16:23:20.933,16:23:20.933,0,151,16:23:20.933,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3075,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=33816576&r=169843&adid=92845690&reid=843522403&arid=0&auid=&cn=complete&et=i&_cc=&tpos=0&async=0&init=1&iw=&uxnw=169843&uxss=vg2400781&uxct=4&metr=1031,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55179,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:20.774,16:23:20.774,0,16:23:20.812,16:23:20.812,0,38,16:23:20.812,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3074,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:55188,map.mp.nbc.com - 151.101.130.49:443,16:23:20.018,16:23:20.076,58,16:23:20.106,16:23:20.107,0,89,16:23:20.107,4930,42,0,62,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3073,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=33816576&r=169843&adid=92845690&reid=843522403&arid=0&auid=&cn=thirdQuartile&et=i&_cc=&tpos=0&async=0&init=1&iw=&uxnw=169843&uxss=vg2400781&uxct=4&metr=1031,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55179,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:19.704,16:23:19.704,0,16:23:19.745,16:23:19.745,0,40,16:23:19.745,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3071,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=33816576&r=169843&adid=92845690&reid=843522403&arid=0&auid=&cn=midPoint&et=i&_cc=&tpos=0&async=0&init=1&iw=&uxnw=169843&uxss=vg2400781&uxct=4&metr=1031,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55179,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:18.643,16:23:18.643,0,16:23:18.692,16:23:18.692,0,49,16:23:18.692,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3070,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=33816576&r=169843&adid=92845690&reid=843522403&arid=0&auid=&cn=firstQuartile&et=i&_cc=&tpos=0&async=0&init=1&iw=&uxnw=169843&uxss=vg2400781&uxct=4&metr=1031,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55179,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:17.586,16:23:17.679,93,16:23:17.790,16:23:17.790,0,205,16:23:17.790,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3058,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=5&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=4&ns_st_sp=1&ns_st_sc=1&ns_st_psq=2&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=2&ns_st_ev=play&ns_st_po=0&ns_st_cl=4238&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=1&ns_st_ad=1&ns_st_ci=12014223&ns_st_pt=0&ns_st_dpt=0&ns_st_ipt=0&ns_st_ap=0&ns_st_dap=0&ns_st_et=0&ns_st_det=0&ns_st_upc=0&ns_st_dupc=0&ns_st_iupc=0&ns_st_upa=0&ns_st_dupa=0&ns_st_iupa=0&ns_st_lpc=0&ns_st_dlpc=0&ns_st_lpa=0&ns_st_dlpa=0&ns_st_pa=7957&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999396526&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=0&ns_st_dpc=0&ns_st_pp=1&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_an=1&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=va12&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&ns_st_amg=92830326&ns_st_ami=214161967&ns_st_amp=92845689&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=&cs_ad_tu=https%3A%2F%2Fsb.scorecardresearch.com%2Fp%3Fc1%3D3%26c2%3D28881558%26c3%3D92830326%26c4%3D214161967%26c5%3D92845689%26c12%3D%26ns_ad_vevent%3Dv_start%26ns_ad_pcd%3D4%26ns__t%3D1801516585%26ns__p%3D1801516585%26ns_st_pr%3DNBCS%3A%20CSNPhilly%3A%20Baseball%3A%20Live%3A%20MLB%3A%20Men%26ns_st_ge%3D%26ns_st_pu%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Web%3A%20Live%20Event%26ns_st_ep%3DNBCS%3A%20Live%3A%20CSNPhilly%3A%20Baseball%3A%20MLB%3A%20Non-Broadcast%3A%20Philadelphia%20Phillies%3A%20Washington%20Nationals%3A%20nbc_dtc_12014222%5E%26ns_st_ct%3DNBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Computer%3A%20Web%3A%20Live%20Event%26cs_vp_sv%3D1%26rn%3D1801516585%26ccr%3D1%26ccrsdk%3D1%26c6%3Dmidroll%26ns_ap_device%3D%26ns_ap_pn%3D,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55148,sb.scorecardresearch.com - 99.84.215.5:443,16:23:16.595,16:23:16.765,170,16:23:16.791,16:23:16.791,0,196,16:23:16.791,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3057,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=4&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=3&ns_st_sp=1&ns_st_sc=1&ns_st_psq=1&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=1&ns_st_ev=end&ns_st_po=7957&ns_st_cl=0&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=7957&ns_st_dpt=0&ns_st_ipt=0&ns_st_ap=7957&ns_st_dap=0&ns_st_et=8499&ns_st_det=542&ns_st_upc=7957&ns_st_dupc=0&ns_st_iupc=0&ns_st_upa=7957&ns_st_dupa=0&ns_st_iupa=0&ns_st_lpc=7957&ns_st_dlpc=0&ns_st_lpa=7957&ns_st_dlpa=0&ns_st_pa=7957&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999396526&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=1&ns_st_dpc=0&ns_st_pp=1&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55074,sb.scorecardresearch.com - 99.84.215.5:443,16:23:16.574,16:23:16.574,0,16:23:16.601,16:23:16.601,0,27,16:23:16.601,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3055,https://nbcume.sc.omtrdc.net/b/ss/nbcutve/1/JS-2.24.0/s15683288284250,nbcume.sc.omtrdc.net,/b/ss/nbcutve/1/JS-2.24.0/s15683288284250,Completed,200,POST,image/gif;charset=utf-8,Google Chrome,127.0.0.1:55138,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:23:16.542,16:23:16.709,167,16:23:16.757,16:23:16.757,0,215,16:23:16.757,2172,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
3055,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55083,map.mp.nbc.com - 151.101.130.49:443,16:23:16.541,16:23:16.541,0,16:23:16.570,16:23:16.570,0,29,16:23:16.570,2214,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3054,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55079,map.mp.nbc.com - 151.101.130.49:443,16:23:16.539,16:23:16.539,0,16:23:16.568,16:23:16.568,0,28,16:23:16.568,2192,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3040,https://sb.scorecardresearch.com/p?c1=3&c2=28881558&c3=92830326&c4=214161967&c5=92845689&c12=&ns_ad_vevent=v_start&ns_ad_pcd=4&ns__t=1801516585&ns__p=1801516585&ns_st_pr=NBCS%3A%20CSNPhilly%3A%20Baseball%3A%20Live%3A%20MLB%3A%20Men&ns_st_ge=&ns_st_pu=NBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Web%3A%20Live%20Event&ns_st_ep=NBCS%3A%20Live%3A%20CSNPhilly%3A%20Baseball%3A%20MLB%3A%20Non-Broadcast%3A%20Philadelphia%20Phillies%3A%20Washington%20Nationals%3A%20nbc_dtc_12014222%5E&ns_st_ct=NBCU%3A%20One%20App%3A%20On%20Domain%3A%20Desktop%3A%20Computer%3A%20Web%3A%20Live%20Event&cs_vp_sv=1&rn=1801516585&ccr=1&ccrsdk=1&c6=midroll&ns_ap_device=&ns_ap_pn=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55074,sb.scorecardresearch.com - 99.84.215.5:443,16:23:16.530,16:23:16.530,0,16:23:16.564,16:23:16.564,0,34,16:23:16.564,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3039,"https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=33816576&r=169843&adid=92845690&reid=843522403&arid=0&auid=&cn=defaultImpression&et=i&_cc=92845690,843522403,,,1774999391,1&tpos=0&async=0&iw=&uxnw=169843&uxss=vg2400781&uxct=4&metr=1031&init=1&vcid2=874355218828044267&pingids=2018,3831",29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55124,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:16.530,16:23:16.530,0,16:23:16.574,16:23:16.574,0,44,16:23:16.574,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3037,https://29773.v.fwmrm.net/ad/l/1?s=w8c03&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999391225685334&f=262144&cn=slotImpression&et=i&tpos=0&async=0&init=1&slid=0,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55124,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,16:23:15.995,16:23:16.095,100,16:23:16.180,16:23:16.180,0,184,16:23:16.180,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd07:8e37:a89c:e407:72f:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3037,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55083,map.mp.nbc.com - 151.101.130.49:443,16:23:15.991,16:23:15.992,0,16:23:16.028,16:23:16.028,0,36,16:23:16.028,1184,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3036,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55079,map.mp.nbc.com - 151.101.130.49:443,16:23:15.991,16:23:15.991,0,16:23:16.025,16:23:16.025,0,34,16:23:16.025,1236,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3034,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=3&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=2&ns_st_sp=1&ns_st_sc=1&ns_st_psq=1&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=1&ns_st_ev=pause&ns_st_po=7957&ns_st_cl=0&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=7957&ns_st_dpt=7957&ns_st_ipt=7957&ns_st_ap=7957&ns_st_dap=7957&ns_st_et=7957&ns_st_det=7957&ns_st_upc=7957&ns_st_dupc=7957&ns_st_iupc=7957&ns_st_upa=7957&ns_st_dupa=7957&ns_st_iupa=7957&ns_st_lpc=7957&ns_st_dlpc=7957&ns_st_lpa=7957&ns_st_dlpa=7957&ns_st_pa=7957&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999395984&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=1&ns_st_dpc=1&ns_st_pp=1&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55074,sb.scorecardresearch.com - 99.84.215.5:443,16:23:15.989,16:23:15.989,0,16:23:16.024,16:23:16.024,0,35,16:23:16.024,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3032,https://69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com,,Completed,200,CONNECT,,Google Chrome,127.0.0.1:55111,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,16:23:10.931,16:23:10.975,44,16:23:10.975,16:23:10.975,0,44,16:23:10.975,0,0,0,0,,69af625f11604bac9af691cd670a8c8d.mediatailor.us-west-2.amazonaws.com - 184.33.133.133:443,,,
|
||||
3031,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:55079,map.mp.nbc.com - 151.101.130.49:443,16:23:09.975,16:23:09.975,0,16:23:10.006,16:23:10.006,0,31,16:23:10.006,6346,42,0,62,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3028,https://www.nbc.com/generetic/images/player/back5.svg,www.nbc.com,/generetic/images/player/back5.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55076,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.345,16:23:08.345,0,16:23:08.555,16:23:08.558,3,213,16:23:08.558,0,1446,0,677,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3027,https://www.nbc.com/generetic/images/player/ahead5.svg,www.nbc.com,/generetic/images/player/ahead5.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55075,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.215,16:23:08.215,0,16:23:08.368,16:23:08.368,0,153,16:23:08.368,0,1491,0,690,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3026,https://www.nbc.com/generetic/images/player/ahead10.svg,www.nbc.com,/generetic/images/player/ahead10.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55077,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.214,16:23:08.214,0,16:23:08.344,16:23:08.344,0,130,16:23:08.344,0,1697,0,710,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3024,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55083,map.mp.nbc.com - 151.101.130.49:443,16:23:08.053,16:23:08.166,113,16:23:08.193,16:23:08.193,0,139,16:23:08.193,1270,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3023,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55085,map.mp.nbc.com - 151.101.130.49:443,16:23:08.053,16:23:08.152,99,16:23:08.185,16:23:08.186,0,132,16:23:08.186,1322,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3022,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55084,map.mp.nbc.com - 151.101.130.49:443,16:23:08.053,16:23:08.138,85,16:23:08.169,16:23:08.169,0,116,16:23:08.169,1407,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3021,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55082,map.mp.nbc.com - 151.101.130.49:443,16:23:08.053,16:23:08.152,100,16:23:08.180,16:23:08.180,0,127,16:23:08.180,1578,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3020,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55081,map.mp.nbc.com - 151.101.130.49:443,16:23:08.052,16:23:08.146,94,16:23:08.176,16:23:08.176,0,124,16:23:08.176,1814,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3024,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:55079,map.mp.nbc.com - 151.101.130.49:443,16:23:08.051,16:23:08.165,114,16:23:08.195,16:23:08.195,0,144,16:23:08.195,1587,0,0,0,,map.mp.nbc.com - 151.101.130.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
3020,https://www.nbc.com/generetic/images/player/fullscreen.svg,www.nbc.com,/generetic/images/player/fullscreen.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55078,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.048,16:23:08.112,64,16:23:08.839,16:23:08.840,0,792,16:23:08.840,0,579,0,323,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3018,https://www.nbc.com/generetic/images/player/keyboard.svg,www.nbc.com,/generetic/images/player/keyboard.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55077,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.047,16:23:08.115,68,16:23:08.214,16:23:08.214,0,166,16:23:08.214,0,1014,0,438,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3016,https://www.nbc.com/generetic/images/player/cc.svg,www.nbc.com,/generetic/images/player/cc.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55076,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.047,16:23:08.110,63,16:23:08.344,16:23:08.344,0,298,16:23:08.344,0,801,0,444,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3015,https://www.nbc.com/generetic/images/player/volume.svg,www.nbc.com,/generetic/images/player/volume.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55075,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.046,16:23:08.118,72,16:23:08.215,16:23:08.215,0,169,16:23:08.215,0,508,0,270,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3014,https://sb.scorecardresearch.com/p?c1=2&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=foreground&ns_ap_ec=2&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=hidden&ns_radio=unknown&ns_nc=1&ns_st_sv=6.3.4.190424&ns_st_smv=5.10&ns_st_it=r&ns_st_id=1774999385980&ns_st_ec=1&ns_st_sp=1&ns_st_sc=1&ns_st_psq=1&ns_st_asq=1&ns_st_sq=1&ns_st_ppc=1&ns_st_apc=1&ns_st_spc=1&ns_st_cn=1&ns_st_ev=play&ns_st_po=0&ns_st_cl=0&ns_st_pb=1&ns_st_mp=js_api&ns_st_mv=6.3.4.190424&ns_st_pn=1&ns_st_tp=0&ns_st_li=1&ns_st_ci=12014223&ns_st_pt=0&ns_st_dpt=0&ns_st_ipt=0&ns_st_ap=0&ns_st_dap=0&ns_st_et=0&ns_st_det=0&ns_st_upc=0&ns_st_dupc=0&ns_st_iupc=0&ns_st_upa=0&ns_st_dupa=0&ns_st_iupa=0&ns_st_lpc=0&ns_st_dlpc=0&ns_st_lpa=0&ns_st_dlpa=0&ns_st_pa=0&ns_st_ldw=0&ns_st_ldo=0&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_cs=1&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999388027&ns_st_bc=0&ns_st_dbc=0&ns_st_bt=0&ns_st_dbt=0&ns_st_bp=0&ns_st_lt=2046&ns_st_skc=0&ns_st_dskc=0&ns_st_ska=0&ns_st_dska=0&ns_st_skd=0&ns_st_skt=0&ns_st_dskt=0&ns_st_pc=0&ns_st_dpc=0&ns_st_pp=0&ns_st_br=0&ns_st_rt=100&ns_st_ub=0&ns_st_ki=1200000&ns_st_pr=MLB&ns_st_sn=None&ns_st_en=None&ns_st_ep=Nationals%20vs.%20Phillies&ns_st_ct=vc13&ns_st_ge=Sports%2C%20Baseball&ns_st_st=NBC%20Sports%20Philadelphia&ns_st_ce=1&ns_st_ia=0&ns_st_ddt=2026-03-31&ns_st_tdt=2026-03-31&ns_st_pu=NBC%20Sports%20Philadelphia&ns_st_ft=None&c3=NBC%20Sports%20Philadelphia&c4=Browser&c6=NBC%20Sports%20Philadelphia_MLB_Sports%2C%20Baseball&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=&cs_ucfr=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:55074,sb.scorecardresearch.com - 99.84.215.5:443,16:23:08.046,16:23:08.107,61,16:23:08.140,16:23:08.140,0,94,16:23:08.140,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
3013,https://www.nbc.com/generetic/images/player/back10.svg,www.nbc.com,/generetic/images/player/back10.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:55017,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.041,16:23:08.041,0,16:23:09.363,16:23:09.363,0,1322,16:23:09.363,0,727,0,394,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3012,https://www.nbc.com/generetic/images/player/pause.svg,www.nbc.com,/generetic/images/player/pause.svg,Completed,200,GET,image/svg+xml,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:08.041,16:23:08.041,0,16:23:08.527,16:23:08.528,0,487,16:23:08.528,0,644,0,329,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
3009,https://drmproxy.digitalsvc.apps.nbcuni.com/drm-proxy/license/widevine?time=1774999386044&hash=bf4c81b2ca82d25dde43e12aaa3dbcddae29f0b2cec16d3018c44097d784e3de&device=web,drmproxy.digitalsvc.apps.nbcuni.com,/drm-proxy/license/widevine,Completed,200,POST,application/octet-stream,Google Chrome,127.0.0.1:55065,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,16:23:07.709,16:23:07.709,0,16:23:07.878,16:23:07.878,0,169,16:23:07.878,3942,620,0,0,,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,www.seeso.com,www.seeso.com; *.apps.nbcuni.com; *.bxjyb2jvda.net; *.digitalsvc.apps.nbcuni.com; *.e-corp-usa.com; *.eonline.com; *.evil-corp-usa.com; *.mvpd-admin.nbcuni.com; *.roku.usa.nbcuni.com; *.seeso.com; *.serverfarm.evil-corp-usa.com; *.swe.apps.nbcuni.com; *.syfy.com; *.tvecms.bravo.nbcuni.com; *.tvecms.chiller.nbcuni.com; *.tvecms.cnbc.nbcuni.com; *.tvecms.eonline.nbcuni.com; *.tvecms.esquire.nbcuni.com; *.tvecms.msnbc.nbcuni.com; *.tvecms.nbcuniverso.nbcuni.com; *.tvecms.oxygen.nbcuni.com; *.tvecms.sprout.nbcuni.com; *.tvecms.syfy.nbcuni.com; *.tvecms.telemundo.nbcuni.com; betadev.idxapi.nbcuni.com; betaidentity.apps.nbcuni.com; betaqa.idxapi.nbcuni.com; betastage.idxapi.nbcuni.com; citywalkhollywood.com; conficturaindustries.com; dev.tvecms.usanetwork.nbcuni.com; e-corp-online.com; e-corp-usa.com; eonline.com; evil-corp-usa.com; fsoc.sh; iammrrobot.com; m.citywalkhollywood.com; racksure.com; realtimetranslation.net; seeso.com; stage.syfywire.com; staticfiles.blastr.com; syfywire.com; universalstudios.com; whereismrrobot.com; whoismrrobot.com; www.citywalkhollywood.com; www.conficturaindustries.com; www.e-corp-online.com; www.e-corp-usa.com; www.fastandfurious-hobbsshaw.it; www.fsoc.sh; www.goodboys.ch; www.hobbs-shaw.at; www.hobbsandshaw-lefilm.be; www.hobbsandshaw-lefilm.ch; www.hobbsandshaw-movie.be; www.hobbsandshaw.ch; www.hobbsandshaw.nl; www.hobbsandshawmovie.ph; www.hobbseshaw-ofilme.pt; www.iammrrobot.com; www.racksure.com; www.rapidosyfuriosos-latam.com; www.realtimetranslation.net; www.syfywire.com; www.tudobonsmeninos.pt; www.universalbranddevelopment.com; www.universalstudios.com; www.upi-digital.com; www.whereismrrobot.com; www.whoismrrobot.com; drmproxy.digitalsvc.apps.nbcuni.com,
|
||||
3008,https://drmproxy.digitalsvc.apps.nbcuni.com/drm-proxy/license/widevine?time=1774999386044&hash=bf4c81b2ca82d25dde43e12aaa3dbcddae29f0b2cec16d3018c44097d784e3de&device=web,drmproxy.digitalsvc.apps.nbcuni.com,/drm-proxy/license/widevine,Completed,200,POST,application/octet-stream,Google Chrome,127.0.0.1:55065,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,16:23:07.591,16:23:07.591,0,16:23:07.683,16:23:07.683,0,93,16:23:07.683,2,711,0,0,,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,www.seeso.com,www.seeso.com; *.apps.nbcuni.com; *.bxjyb2jvda.net; *.digitalsvc.apps.nbcuni.com; *.e-corp-usa.com; *.eonline.com; *.evil-corp-usa.com; *.mvpd-admin.nbcuni.com; *.roku.usa.nbcuni.com; *.seeso.com; *.serverfarm.evil-corp-usa.com; *.swe.apps.nbcuni.com; *.syfy.com; *.tvecms.bravo.nbcuni.com; *.tvecms.chiller.nbcuni.com; *.tvecms.cnbc.nbcuni.com; *.tvecms.eonline.nbcuni.com; *.tvecms.esquire.nbcuni.com; *.tvecms.msnbc.nbcuni.com; *.tvecms.nbcuniverso.nbcuni.com; *.tvecms.oxygen.nbcuni.com; *.tvecms.sprout.nbcuni.com; *.tvecms.syfy.nbcuni.com; *.tvecms.telemundo.nbcuni.com; betadev.idxapi.nbcuni.com; betaidentity.apps.nbcuni.com; betaqa.idxapi.nbcuni.com; betastage.idxapi.nbcuni.com; citywalkhollywood.com; conficturaindustries.com; dev.tvecms.usanetwork.nbcuni.com; e-corp-online.com; e-corp-usa.com; eonline.com; evil-corp-usa.com; fsoc.sh; iammrrobot.com; m.citywalkhollywood.com; racksure.com; realtimetranslation.net; seeso.com; stage.syfywire.com; staticfiles.blastr.com; syfywire.com; universalstudios.com; whereismrrobot.com; whoismrrobot.com; www.citywalkhollywood.com; www.conficturaindustries.com; www.e-corp-online.com; www.e-corp-usa.com; www.fastandfurious-hobbsshaw.it; www.fsoc.sh; www.goodboys.ch; www.hobbs-shaw.at; www.hobbsandshaw-lefilm.be; www.hobbsandshaw-lefilm.ch; www.hobbsandshaw-movie.be; www.hobbsandshaw.ch; www.hobbsandshaw.nl; www.hobbsandshawmovie.ph; www.hobbseshaw-ofilme.pt; www.iammrrobot.com; www.racksure.com; www.rapidosyfuriosos-latam.com; www.realtimetranslation.net; www.syfywire.com; www.tudobonsmeninos.pt; www.universalbranddevelopment.com; www.universalstudios.com; www.upi-digital.com; www.whereismrrobot.com; www.whoismrrobot.com; drmproxy.digitalsvc.apps.nbcuni.com,
|
||||
3007,https://drmproxy.digitalsvc.apps.nbcuni.com/drm-proxy/license/widevine?time=1774999386044&hash=bf4c81b2ca82d25dde43e12aaa3dbcddae29f0b2cec16d3018c44097d784e3de&device=web,drmproxy.digitalsvc.apps.nbcuni.com,/drm-proxy/license/widevine,Completed,200,OPTIONS,application/json,Google Chrome,127.0.0.1:55065,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,16:23:07.428,16:23:07.476,48,16:23:07.590,16:23:07.590,0,162,16:23:07.590,0,0,0,0,,drmproxy.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,www.seeso.com,www.seeso.com; *.apps.nbcuni.com; *.bxjyb2jvda.net; *.digitalsvc.apps.nbcuni.com; *.e-corp-usa.com; *.eonline.com; *.evil-corp-usa.com; *.mvpd-admin.nbcuni.com; *.roku.usa.nbcuni.com; *.seeso.com; *.serverfarm.evil-corp-usa.com; *.swe.apps.nbcuni.com; *.syfy.com; *.tvecms.bravo.nbcuni.com; *.tvecms.chiller.nbcuni.com; *.tvecms.cnbc.nbcuni.com; *.tvecms.eonline.nbcuni.com; *.tvecms.esquire.nbcuni.com; *.tvecms.msnbc.nbcuni.com; *.tvecms.nbcuniverso.nbcuni.com; *.tvecms.oxygen.nbcuni.com; *.tvecms.sprout.nbcuni.com; *.tvecms.syfy.nbcuni.com; *.tvecms.telemundo.nbcuni.com; betadev.idxapi.nbcuni.com; betaidentity.apps.nbcuni.com; betaqa.idxapi.nbcuni.com; betastage.idxapi.nbcuni.com; citywalkhollywood.com; conficturaindustries.com; dev.tvecms.usanetwork.nbcuni.com; e-corp-online.com; e-corp-usa.com; eonline.com; evil-corp-usa.com; fsoc.sh; iammrrobot.com; m.citywalkhollywood.com; racksure.com; realtimetranslation.net; seeso.com; stage.syfywire.com; staticfiles.blastr.com; syfywire.com; universalstudios.com; whereismrrobot.com; whoismrrobot.com; www.citywalkhollywood.com; www.conficturaindustries.com; www.e-corp-online.com; www.e-corp-usa.com; www.fastandfurious-hobbsshaw.it; www.fsoc.sh; www.goodboys.ch; www.hobbs-shaw.at; www.hobbsandshaw-lefilm.be; www.hobbsandshaw-lefilm.ch; www.hobbsandshaw-movie.be; www.hobbsandshaw.ch; www.hobbsandshaw.nl; www.hobbsandshawmovie.ph; www.hobbseshaw-ofilme.pt; www.iammrrobot.com; www.racksure.com; www.rapidosyfuriosos-latam.com; www.realtimetranslation.net; www.syfywire.com; www.tudobonsmeninos.pt; www.universalbranddevelopment.com; www.universalstudios.com; www.upi-digital.com; www.whereismrrobot.com; www.whoismrrobot.com; drmproxy.digitalsvc.apps.nbcuni.com,
|
||||
3003,"https://29773.v.fwmrm.net/ad/l/1?s=wad20&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999386141630193&f=262144&cn=slotEnd&et=i&tpos=0&async=0&init=1&slid=0,1",29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55046,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,16:23:07.111,16:23:07.270,159,16:23:07.313,16:23:07.313,0,203,16:23:07.313,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3002,"https://29773.v.fwmrm.net/ad/l/1?s=wad20&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999386141630193&f=262144&cn=slotImpression&et=i&tpos=0&async=0&init=1&slid=0,1",29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55048,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,16:23:07.098,16:23:07.192,94,16:23:07.305,16:23:07.305,0,206,16:23:07.305,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
3001,https://29773.v.fwmrm.net/ad/l/1?s=wad20&n=169843%3B169843%3B147530%3B190200%3B372496%3B376521%3B378678%3B378901%3B379619%3B380903%3B381963%3B382114%3B382926%3B384777%3B386329%3B391823%3B393638%3B499607%3B505334%3B510702%3B510839%3B511345%3B512029%3B515123%3B515154%3B516448%3B529773&t=1774999386141630193&f=262144&cn=videoView&et=i&uxnw=169843&uxss=vg2400781&uxct=4&init=1&vcid2=874355218828044267,29773.v.fwmrm.net,/ad/l/1,Completed,200,GET,text/html,Google Chrome,127.0.0.1:55047,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,16:23:07.086,16:23:07.185,100,16:23:07.269,16:23:07.269,0,183,16:23:07.269,0,0,0,0,,29773.v.fwmrm.net - 2600:1f14:c96:cd06:af96:f83e:4305:7629:443,*.v.fwmrm.net,*.v.fwmrm.net; 29773.v.fwmrm.net,
|
||||
2999,https://nbcume.sc.omtrdc.net/b/ss/nbcutve/1/JS-2.24.0/s13469709661995?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A23%3A6%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=UTF-8&g=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c.&videoapp=NBC%20Network%20App&appVersion=1.249.0&playerVersion=v3.3.10-v102.4&videoplatform=Web&videoguid=nbc_dtc_12014222&videosubcat2=Sports%2C%20Baseball&videosubcat1=Sports&videonetwork=NBC%20Sports%20Philadelphia&videoprogram=MLB&videoseason=None&videoepnumber=None&videotitle=Nationals%20vs.%20Phillies&a.&media.&friendlyName=Nationals%20vs.%20Phillies&length=86400&asset=POPUP%3A12014223&streamType=video&name=nbc_dtc_12014222&playerName=CVSDK%20Javascript%20Player%20-%20Shaka&channel=On-Domain&view=true&vsid=1774999386913920330261&.media&contentType=live&.a&videoinitiate=Auto-play&videodate=03-31-26&videoday=Tuesday&videohour=16%3A00&videominute=23&videoairdate=03-31-26&videostatus=Entitled&videoplayerurl=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&videodomain=https%3A%2F%2Fwww.nbc.com&videocallsign=None&videomvpd=Verizon&videotmsid=None&videocliptype=unknown&videoscreen=Normal&videosport=Baseball&videoleague=MLB&videobroadcast=Digital&videodaypart=None&videoplayertech=DASH&videocastsource=N%2FA&videocrossdevice=F&videolanguage=N%2FA&videopassguid=5525ff59adcaac313923ab89d0a618c5&videorequestorid=nbcentertainment&videoresearchtitle=260331%20-%20rsn-philadelphia%20-%20Nationals%20vs.%20Phillies&videosponsor=N%2FA&.c&aamb=RKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y&pe=ms_s&pev3=video&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&AQE=1,nbcume.sc.omtrdc.net,/b/ss/nbcutve/1/JS-2.24.0/s13469709661995,Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:55051,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:23:06.922,16:23:07.026,104,16:23:07.077,16:23:07.077,0,155,16:23:07.077,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2996,https://mt.ssai-oneapp.nbcuni.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?mt.config=oneapp-atp-dash-sle-4s-generic&audio=all&subtitle=all&forcedNarrative=true,mt.ssai-oneapp.nbcuni.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,200,POST,application/json,Google Chrome,127.0.0.1:55042,mt.ssai-oneapp.nbcuni.com - 2600:9000:211d:ce00:a:768a:2640:93a1:443,16:23:06.743,16:23:06.743,0,16:23:06.898,16:23:06.898,0,156,16:23:06.898,2148,636,0,0,,mt.ssai-oneapp.nbcuni.com - 2600:9000:211d:ce00:a:768a:2640:93a1:443,mt.ssai-oneapp.nbcuni.com,mt.ssai-oneapp.nbcuni.com,
|
||||
2995,https://mt.ssai-oneapp.nbcuni.com/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd?mt.config=oneapp-atp-dash-sle-4s-generic&audio=all&subtitle=all&forcedNarrative=true,mt.ssai-oneapp.nbcuni.com,/Content/CMAF_OS1-CTR-4s-v2/Live/channel(12014223-12014222-444afee22018e)/master_2hr.mpd,Completed,204,OPTIONS,application/json,Google Chrome,127.0.0.1:55042,mt.ssai-oneapp.nbcuni.com - 2600:9000:211d:ce00:a:768a:2640:93a1:443,16:23:06.574,16:23:06.660,85,16:23:06.741,16:23:06.741,0,167,16:23:06.741,0,0,0,0,,mt.ssai-oneapp.nbcuni.com - 2600:9000:211d:ce00:a:768a:2640:93a1:443,mt.ssai-oneapp.nbcuni.com,mt.ssai-oneapp.nbcuni.com,
|
||||
2991,https://www.nbc.com/generetic/scripts/omweb-v1.js,www.nbc.com,/generetic/scripts/omweb-v1.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:06.092,16:23:06.092,0,16:23:06.141,16:23:06.141,0,48,16:23:06.141,0,69498,0,14906,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2987,https://sb.scorecardresearch.com/p2?c1=19&c2=6035083&ns_ap_an=NBC%20Network%20App&ns_ap_pn=Mac%20OS&ns_ap_pv=5&c12=d6cb60632d35f07ce7f1951dcd3dd8b2-cs72&name=start&ns_ap_ec=1&ns_ap_ev=start&ns_ap_device=MacIntel&ns_ap_id=1774999385979&ns_ap_csf=1&ns_ap_bi=&ns_ap_pfm=html&ns_ap_pfv=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&ns_ap_ver=1.249.0&ns_ap_sv=6.3.4.190424&ns_type=view&ns_radio=unknown&ns_nc=1&ns_ap_gs=1774999385979&ns_ap_jb=unknown&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_install=1774999385979&ns_ap_lastrun=0&ns_ap_cs=1&ns_ap_runs=1&ns_ap_usage=0&ns_ap_fg=1&ns_ap_ft=0&ns_ap_dft=0&ns_ap_bt=0&ns_ap_dbt=0&ns_ap_dit=0&ns_ap_as=1&ns_ap_das=0&ns_ap_it=0&ns_ap_ut=60000&ns_ap_lang=en-US&ns_ap_ar=unknown&ns_ts=1774999385979&cs_ucfr=,sb.scorecardresearch.com,/p2,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:54909,sb.scorecardresearch.com - 99.84.215.5:443,16:23:05.993,16:23:05.993,0,16:23:06.024,16:23:06.024,0,31,16:23:06.024,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2986,https://nbcume.sc.omtrdc.net/id?d_visid_ver=5.5.0&d_fieldgroup=A&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&mid=48149800880344474781461921042733640603&ts=1774999385978,nbcume.sc.omtrdc.net,/id,Completed,200,GET,application/x-javascript;charset=utf-8,Google Chrome,127.0.0.1:54988,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:23:05.979,16:23:05.979,0,16:23:06.027,16:23:06.027,0,48,16:23:06.027,0,2,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2985,https://www.nbc.com/watch/mlb/nationals-vs-phillies/omweb-v1.js,www.nbc.com,/watch/mlb/nationals-vs-phillies/omweb-v1.js,Redirect,301,GET,text/plain; charset=utf-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:05.976,16:23:05.976,0,16:23:06.092,16:23:06.092,0,116,16:23:06.092,0,64,0,0,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2984,https://www.nbc.com/generetic/generated/chunks/8452.e13c296f6759707ba8b0.js,www.nbc.com,/generetic/generated/chunks/8452.e13c296f6759707ba8b0.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:05.616,16:23:05.616,0,16:23:05.693,16:23:05.697,3,81,16:23:05.697,0,361538,0,117246,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2983,https://www.nbc.com/generetic/generated/chunks/7738.8e268cc07398c51c8aec.js,www.nbc.com,/generetic/generated/chunks/7738.8e268cc07398c51c8aec.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55017,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.347,16:23:04.347,0,16:23:05.478,16:23:05.479,0,1131,16:23:05.479,0,213,0,111,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2982,https://www.nbc.com/generetic/generated/chunks/617.39fd88239a001e5f4fd4.js,www.nbc.com,/generetic/generated/chunks/617.39fd88239a001e5f4fd4.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:04.289,16:23:04.290,0,16:23:04.371,16:23:04.391,20,101,16:23:04.391,0,1079237,0,157916,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2981,https://www.nbc.com/generetic/generated/chunks/2612.639ec8413ef63217203f.js,www.nbc.com,/generetic/generated/chunks/2612.639ec8413ef63217203f.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55017,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.289,16:23:04.289,0,16:23:04.346,16:23:04.346,0,57,16:23:04.346,0,32981,0,5848,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2980,https://www.nbc.com/generetic/generated/chunks/6852.8e8d78ca9e5ee5b2d393.js,www.nbc.com,/generetic/generated/chunks/6852.8e8d78ca9e5ee5b2d393.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55016,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.289,16:23:04.289,0,16:23:05.247,16:23:05.248,0,958,16:23:05.248,0,87328,0,13357,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2979,https://www.nbc.com/generetic/generated/chunks/1754.21ae655dbc3875b3a9b2.js,www.nbc.com,/generetic/generated/chunks/1754.21ae655dbc3875b3a9b2.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.289,16:23:04.289,0,16:23:05.607,16:23:05.607,0,1318,16:23:05.607,0,36320,0,6488,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2978,https://www.nbc.com/generetic/generated/chunks/9858.8170d73af8c99769dedb.js,www.nbc.com,/generetic/generated/chunks/9858.8170d73af8c99769dedb.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:04.289,16:23:04.289,0,16:23:04.399,16:23:04.399,0,110,16:23:04.399,0,58024,0,8577,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2977,https://www.nbc.com/generetic/generated/chunks/3589.00a9e4a448d3d02686c5.js,www.nbc.com,/generetic/generated/chunks/3589.00a9e4a448d3d02686c5.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:04.289,16:23:04.289,0,16:23:04.441,16:23:04.442,1,154,16:23:04.442,0,198083,0,29560,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2976,https://www.nbc.com/generetic/generated/chunks/865.7fa93d2fedca26256bf0.js,www.nbc.com,/generetic/generated/chunks/865.7fa93d2fedca26256bf0.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:04.166,16:23:04.166,0,16:23:04.274,16:23:04.274,0,108,16:23:04.274,0,7906,0,1948,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2975,https://www.nbc.com/generetic/generated/chunks/1447.b265bc15ed7d3ea313bc.js,www.nbc.com,/generetic/generated/chunks/1447.b265bc15ed7d3ea313bc.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:04.166,16:23:04.166,0,16:23:04.284,16:23:04.284,0,118,16:23:04.284,0,77794,0,12273,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2974,https://www.nbc.com/generetic/generated/chunks/1920.14e324e388b3e79b5e58.js,www.nbc.com,/generetic/generated/chunks/1920.14e324e388b3e79b5e58.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55016,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.166,16:23:04.166,0,16:23:04.210,16:23:04.210,0,44,16:23:04.210,0,132209,0,19191,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2973,https://www.nbc.com/generetic/generated/chunks/5161.d63a41f9ec36bd3bfa9e.js,www.nbc.com,/generetic/generated/chunks/5161.d63a41f9ec36bd3bfa9e.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:04.165,16:23:04.165,0,16:23:04.230,16:23:04.230,0,65,16:23:04.230,0,19003,0,5038,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2972,https://www.nbc.com/generetic/generated/chunks/4912.32c763cf22f4de975f0e.js,www.nbc.com,/generetic/generated/chunks/4912.32c763cf22f4de975f0e.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55016,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.840,16:23:02.840,0,16:23:03.504,16:23:03.505,1,665,16:23:03.505,0,100082,0,13919,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2971,https://www.nbc.com/generetic/generated/chunks/2881.ff0e42b4075313d52d3a.js,www.nbc.com,/generetic/generated/chunks/2881.ff0e42b4075313d52d3a.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.453,16:23:02.453,0,16:23:02.550,16:23:02.550,0,97,16:23:02.550,0,86480,0,13096,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2970,https://www.nbc.com/generetic/generated/chunks/7464.cf6f9a934559a3ce6550.js,www.nbc.com,/generetic/generated/chunks/7464.cf6f9a934559a3ce6550.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.360,16:23:02.360,0,16:23:02.427,16:23:02.427,0,67,16:23:02.427,0,5498,0,1547,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2969,https://www.nbc.com/generetic/generated/chunks/6741.dedf6c0bfe69723b1308.js,www.nbc.com,/generetic/generated/chunks/6741.dedf6c0bfe69723b1308.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.322,16:23:02.322,0,16:23:02.587,16:23:02.588,0,266,16:23:02.588,0,36470,0,6255,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2968,https://www.nbc.com/generetic/generated/chunks/5275.73a6acb807d0c7218823.js,www.nbc.com,/generetic/generated/chunks/5275.73a6acb807d0c7218823.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.314,16:23:02.314,0,16:23:04.160,16:23:04.160,0,1847,16:23:04.160,0,23660,0,4737,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2966,https://www.nbc.com/generetic/generated/chunks/9855.d5d6f20b438e882d7d18.js,www.nbc.com,/generetic/generated/chunks/9855.d5d6f20b438e882d7d18.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55017,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.277,16:23:02.337,60,16:23:02.547,16:23:02.548,0,271,16:23:02.548,0,63013,0,9907,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2967,https://www.nbc.com/generetic/generated/chunks/6028.51e0b3aacc415d474601.js,www.nbc.com,/generetic/generated/chunks/6028.51e0b3aacc415d474601.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:55016,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.277,16:23:02.334,57,16:23:02.831,16:23:02.833,2,556,16:23:02.833,0,186497,0,35036,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2965,https://www.nbc.com/generetic/generated/chunks/9558.74c80de93e916d34668a.js,www.nbc.com,/generetic/generated/chunks/9558.74c80de93e916d34668a.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.270,16:23:02.270,0,16:23:02.450,16:23:02.451,0,180,16:23:02.451,0,59968,0,7714,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2964,https://www.nbc.com/generetic/generated/chunks/7498.30e5f63ed16c5dc8728e.js,www.nbc.com,/generetic/generated/chunks/7498.30e5f63ed16c5dc8728e.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.270,16:23:02.270,0,16:23:02.320,16:23:02.320,0,50,16:23:02.320,0,60011,0,9182,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2963,https://www.nbc.com/generetic/generated/chunks/6064.489101561449c5ec8ace.js,www.nbc.com,/generetic/generated/chunks/6064.489101561449c5ec8ace.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.270,16:23:02.270,0,16:23:02.357,16:23:02.360,3,90,16:23:02.360,0,124563,0,30894,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2962,https://www.nbc.com/generetic/generated/chunks/3932.bbf4be740487f4e4256c.js,www.nbc.com,/generetic/generated/chunks/3932.bbf4be740487f4e4256c.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.270,16:23:02.270,0,16:23:02.313,16:23:02.314,1,44,16:23:02.314,0,91159,0,16918,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2959,https://www.nbc.com/generetic/generated/chunks/2859.f0865b626c12bc9ee1fe.js,www.nbc.com,/generetic/generated/chunks/2859.f0865b626c12bc9ee1fe.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.073,16:23:02.074,0,16:23:02.222,16:23:02.222,0,149,16:23:02.222,0,43750,0,7285,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2958,https://www.nbc.com/generetic/generated/chunks/4160.dbd7af47207bdf03c69b.js,www.nbc.com,/generetic/generated/chunks/4160.dbd7af47207bdf03c69b.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.073,16:23:02.074,0,16:23:02.158,16:23:02.160,2,87,16:23:02.160,0,173470,0,30990,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2957,https://www.nbc.com/generetic/generated/chunks/7988.78bc6efd346c51993e4f.js,www.nbc.com,/generetic/generated/chunks/7988.78bc6efd346c51993e4f.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.073,16:23:02.074,0,16:23:02.237,16:23:02.268,31,194,16:23:02.268,0,1101411,0,178913,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2956,https://www.nbc.com/generetic/generated/chunks/4706.4d1a2ce67f3ce19f0732.js,www.nbc.com,/generetic/generated/chunks/4706.4d1a2ce67f3ce19f0732.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:23:02.073,16:23:02.074,0,16:23:02.224,16:23:02.225,0,151,16:23:02.225,0,78006,0,8581,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2955,https://www.nbc.com/generetic/generated/chunks/5695.b062340e3944f1931722.js,www.nbc.com,/generetic/generated/chunks/5695.b062340e3944f1931722.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:23:02.010,16:23:02.010,0,16:23:02.070,16:23:02.070,0,60,16:23:02.070,0,2637,0,975,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2951,https://tokenverifier.digitalsvc.apps.nbcuni.com/tokenverifier/entitlement/verifier,tokenverifier.digitalsvc.apps.nbcuni.com,/tokenverifier/entitlement/verifier,Completed,200,POST,application/media.entitlement-v1+json,Google Chrome,127.0.0.1:54999,tokenverifier.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,16:23:01.500,16:23:01.500,0,16:23:01.586,16:23:01.586,0,86,16:23:01.586,1024,452,0,0,,tokenverifier.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,www.seeso.com,www.seeso.com; *.apps.nbcuni.com; *.bxjyb2jvda.net; *.digitalsvc.apps.nbcuni.com; *.e-corp-usa.com; *.eonline.com; *.evil-corp-usa.com; *.mvpd-admin.nbcuni.com; *.roku.usa.nbcuni.com; *.seeso.com; *.serverfarm.evil-corp-usa.com; *.swe.apps.nbcuni.com; *.syfy.com; *.tvecms.bravo.nbcuni.com; *.tvecms.chiller.nbcuni.com; *.tvecms.cnbc.nbcuni.com; *.tvecms.eonline.nbcuni.com; *.tvecms.esquire.nbcuni.com; *.tvecms.msnbc.nbcuni.com; *.tvecms.nbcuniverso.nbcuni.com; *.tvecms.oxygen.nbcuni.com; *.tvecms.sprout.nbcuni.com; *.tvecms.syfy.nbcuni.com; *.tvecms.telemundo.nbcuni.com; betadev.idxapi.nbcuni.com; betaidentity.apps.nbcuni.com; betaqa.idxapi.nbcuni.com; betastage.idxapi.nbcuni.com; citywalkhollywood.com; conficturaindustries.com; dev.tvecms.usanetwork.nbcuni.com; e-corp-online.com; e-corp-usa.com; eonline.com; evil-corp-usa.com; fsoc.sh; iammrrobot.com; m.citywalkhollywood.com; racksure.com; realtimetranslation.net; seeso.com; stage.syfywire.com; staticfiles.blastr.com; syfywire.com; universalstudios.com; whereismrrobot.com; whoismrrobot.com; www.citywalkhollywood.com; www.conficturaindustries.com; www.e-corp-online.com; www.e-corp-usa.com; www.fastandfurious-hobbsshaw.it; www.fsoc.sh; www.goodboys.ch; www.hobbs-shaw.at; www.hobbsandshaw-lefilm.be; www.hobbsandshaw-lefilm.ch; www.hobbsandshaw-movie.be; www.hobbsandshaw.ch; www.hobbsandshaw.nl; www.hobbsandshawmovie.ph; www.hobbseshaw-ofilme.pt; www.iammrrobot.com; www.racksure.com; www.rapidosyfuriosos-latam.com; www.realtimetranslation.net; www.syfywire.com; www.tudobonsmeninos.pt; www.universalbranddevelopment.com; www.universalstudios.com; www.upi-digital.com; www.whereismrrobot.com; www.whoismrrobot.com; tokenverifier.digitalsvc.apps.nbcuni.com,
|
||||
2949,https://tokenverifier.digitalsvc.apps.nbcuni.com/tokenverifier/entitlement/verifier,tokenverifier.digitalsvc.apps.nbcuni.com,/tokenverifier/entitlement/verifier,Completed,200,OPTIONS,application/json,Google Chrome,127.0.0.1:54999,tokenverifier.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,16:23:01.355,16:23:01.402,47,16:23:01.499,16:23:01.499,0,144,16:23:01.499,0,0,0,0,,tokenverifier.digitalsvc.apps.nbcuni.com - 2600:1406:4c00:199::1297:443,www.seeso.com,www.seeso.com; *.apps.nbcuni.com; *.bxjyb2jvda.net; *.digitalsvc.apps.nbcuni.com; *.e-corp-usa.com; *.eonline.com; *.evil-corp-usa.com; *.mvpd-admin.nbcuni.com; *.roku.usa.nbcuni.com; *.seeso.com; *.serverfarm.evil-corp-usa.com; *.swe.apps.nbcuni.com; *.syfy.com; *.tvecms.bravo.nbcuni.com; *.tvecms.chiller.nbcuni.com; *.tvecms.cnbc.nbcuni.com; *.tvecms.eonline.nbcuni.com; *.tvecms.esquire.nbcuni.com; *.tvecms.msnbc.nbcuni.com; *.tvecms.nbcuniverso.nbcuni.com; *.tvecms.oxygen.nbcuni.com; *.tvecms.sprout.nbcuni.com; *.tvecms.syfy.nbcuni.com; *.tvecms.telemundo.nbcuni.com; betadev.idxapi.nbcuni.com; betaidentity.apps.nbcuni.com; betaqa.idxapi.nbcuni.com; betastage.idxapi.nbcuni.com; citywalkhollywood.com; conficturaindustries.com; dev.tvecms.usanetwork.nbcuni.com; e-corp-online.com; e-corp-usa.com; eonline.com; evil-corp-usa.com; fsoc.sh; iammrrobot.com; m.citywalkhollywood.com; racksure.com; realtimetranslation.net; seeso.com; stage.syfywire.com; staticfiles.blastr.com; syfywire.com; universalstudios.com; whereismrrobot.com; whoismrrobot.com; www.citywalkhollywood.com; www.conficturaindustries.com; www.e-corp-online.com; www.e-corp-usa.com; www.fastandfurious-hobbsshaw.it; www.fsoc.sh; www.goodboys.ch; www.hobbs-shaw.at; www.hobbsandshaw-lefilm.be; www.hobbsandshaw-lefilm.ch; www.hobbsandshaw-movie.be; www.hobbsandshaw.ch; www.hobbsandshaw.nl; www.hobbsandshawmovie.ph; www.hobbseshaw-ofilme.pt; www.iammrrobot.com; www.racksure.com; www.rapidosyfuriosos-latam.com; www.realtimetranslation.net; www.syfywire.com; www.tudobonsmeninos.pt; www.universalbranddevelopment.com; www.universalstudios.com; www.upi-digital.com; www.whereismrrobot.com; www.whoismrrobot.com; tokenverifier.digitalsvc.apps.nbcuni.com,
|
||||
2949,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15228206811507?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A23%3A1%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=nbcentertainment%3APC%3AWatch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&passmvpd=Verizon&passguid=1d547c24870d5152823ebb0c5ca5f837&contenthub=Adobe%20Pass&passnetwork=nbcentertainment&passauthorizesuccess=true&passauthorize=Authorized&title=nbcentertainment%3APC%3AWatch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A23&.tve&.c&cc=USD&pe=lnk_o&pev2=Adobe%20Pass%3AAuthorize%3ASuccess&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=234&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15228206811507",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54988,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:23:01.192,16:23:01.192,0,16:23:01.240,16:23:01.240,0,48,16:23:01.240,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2947,https://sp.auth.adobe.com/adobe-services/shortAuthorize,sp.auth.adobe.com,/adobe-services/shortAuthorize,Completed,200,POST,text/xml;charset=UTF-8,Google Chrome,127.0.0.1:54995,sp.auth.adobe.com - 35.161.249.167:443,16:23:01.131,16:23:01.131,0,16:23:01.182,16:23:01.182,0,51,16:23:01.182,1950,742,0,532,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2946,https://sp.auth.adobe.com/adobe-services/shortAuthorize,sp.auth.adobe.com,/adobe-services/shortAuthorize,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54991,sp.auth.adobe.com - 35.161.249.167:443,16:23:01.090,16:23:01.090,0,16:23:01.130,16:23:01.130,0,40,16:23:01.130,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2945,https://sp.auth.adobe.com/adobe-services/authorize,sp.auth.adobe.com,/adobe-services/authorize,Completed,200,POST,text/xml;charset=UTF-8,Google Chrome,127.0.0.1:54995,sp.auth.adobe.com - 35.161.249.167:443,16:23:00.374,16:23:00.545,171,16:23:01.086,16:23:01.086,0,712,16:23:01.086,2256,1864,0,967,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2944,https://img.nbc.com/files/12012834_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12012834_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54993,img.nbc.com - 2600:1406:5800::173c:a819:443,16:23:00.273,16:23:00.346,73,16:23:00.659,16:23:00.664,5,390,16:23:00.664,0,22883,0,0,,img.nbc.com - 2600:1406:5800::173c:a819:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2943,https://sp.auth.adobe.com/adobe-services/authorize,sp.auth.adobe.com,/adobe-services/authorize,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54991,sp.auth.adobe.com - 35.161.249.167:443,16:23:00.205,16:23:00.332,127,16:23:00.370,16:23:00.370,0,165,16:23:00.370,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2942,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s14602483678589?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A59%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=null%3Avideo%3Aundefined&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&contenthub=Adobe%20Pass&network=NBC%20Entertainment&title=null%3Avideo%3Aundefined&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&nbcu.&contentType=Video&showSiteFeature=Video%20Details%20-%20Short%20Form&.nbcu&.c&cc=USD&server=www.nbc.com&aamb=RKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y&c.&a.&activitymap.&page=global%3Ahome&link=LIVE%20MLB%20Nationals%20vs.%20Phillies%20Started%20at%203%3A00%20PM®ion=main&pageIDType=1&.activitymap&.a&.c&pid=global%3Ahome&pidt=1&oid=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&ot=A&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=194&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s14602483678589",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54988,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:22:59.856,16:23:00.032,176,16:23:00.082,16:23:00.082,0,226,16:23:00.082,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2941,https://mps.nbcuni.com/request/page/json/params/?CALLBACK=mpsCallback&site=nbc-web&type=video-detail&cat=undefined%7Cvideo&pubdate=1774999379&path=%2Ffile%2Fb5cf7d89f6e364db101094be374449528d8138bc&content_id=fileb5cf7d89f6e364db101094be374449528d8138bc&is_content=1&LOADMODE=more&ASYNC=1&_=4&NOLOAD=mpstools&USE_OVERLAY=0&IRSOURCE=false&cag%5Bcontent-type%5D=Single%20Live%20Event&cag%5Bgenre%5D=Sports,mps.nbcuni.com,/request/page/json/params/,Completed,200,GET,application/json; charset=utf-8,Google Chrome,127.0.0.1:54987,mps.nbcuni.com - 23.192.167.152:443,16:22:59.856,16:22:59.980,125,16:23:00.100,16:23:00.100,0,245,16:23:00.100,0,34774,0,9825,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2942,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54953,map.mp.nbc.com - 151.101.2.49:443,16:22:59.853,16:22:59.853,0,16:22:59.958,16:22:59.958,0,105,16:22:59.958,7635,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2941,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54955,map.mp.nbc.com - 151.101.2.49:443,16:22:59.853,16:22:59.853,0,16:22:59.958,16:22:59.958,0,105,16:22:59.958,1341,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2940,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54956,map.mp.nbc.com - 151.101.2.49:443,16:22:59.853,16:22:59.853,0,16:22:59.953,16:22:59.953,0,100,16:22:59.953,1057,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2939,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54954,map.mp.nbc.com - 151.101.2.49:443,16:22:59.852,16:22:59.852,0,16:22:59.958,16:22:59.958,0,105,16:22:59.958,1324,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2937,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999379849&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fwatch%2Fmlb%2Fnationals-vs-phillies%2F12014223&c8=Watch%20Live%3A%20Nationals%20vs.%20Phillies%20-%20NBC.com&c9=,sb.scorecardresearch.com,/b,Completed,204,GET,,Google Chrome,127.0.0.1:54909,sb.scorecardresearch.com - 99.84.215.5:443,16:22:59.851,16:22:59.851,0,16:22:59.925,16:22:59.925,0,75,16:22:59.925,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2935,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Atrue%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22watch%2Fmlb%2Fnationals-vs-phillies%2F12014223%22%2C%22type%22%3A%22STREAM%22%2C%22subType%22%3A%22home%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22appVersion%22%3A1249000%2C%22queryName%22%3A%22componentsForPlaceholders_cached%22%2C%22componentConfigs%22%3A%5B%22eyJmaWx0ZXJWYWx1ZSI6ImFsbCIsImRlZXBMaW5rSGFuZGxlIjoic3BvcnRzLWxpdmVfZ3VpZGVfbWVudXMtLWFsbCIsInR5cGUiOiJTdGFja0dyb3VwIiwiaW1wbGVtZW50YXRpb24iOiJsaXZlR3VpZGVTcG9ydHMiLCJudWxsU3RhdGVNZXNzYWdlIjoiTm8gZXZlbnRzIHNjaGVkdWxlZCB0b2RheS4gQnJvd3NlIGZvciBvdGhlciBMaXZlIFNwb3J0cy4iLCJuYW1lIjoibGl2ZUd1aWRlU3BvcnRzIiwiYXBwIjoibmJjIiwiZmlsdGVyQnkiOiJzcG9ydCJ9%22%5D%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%223b7ab0262b412e1a69aa09333228ebcf55cf4f40a56cff0769607c6f0743a36a%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54984,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:59.848,16:22:59.848,0,16:23:00.238,16:23:00.241,3,393,16:23:00.241,0,441973,0,26722,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2934,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Atrue%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22watch%2Fmlb%2Fnationals-vs-phillies%2F12014223%22%2C%22type%22%3A%22STREAM%22%2C%22subType%22%3A%22home%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22appVersion%22%3A1249000%2C%22queryName%22%3A%22page%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22ac2e08429df2a1b856252b7cc6b38c31975be9b2ad83352e5c74128fb9b8d0ac%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54984,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:59.447,16:22:59.532,85,16:22:59.837,16:22:59.837,0,390,16:22:59.837,0,23644,0,3776,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2933,https://www.nbc.com/generetic/generated/chunks/9451.c7b32c40d56ce5f0648a.js,www.nbc.com,/generetic/generated/chunks/9451.c7b32c40d56ce5f0648a.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54976,www.nbc.com - 2600:1406:5800::173c:a813:443,16:22:55.772,16:22:55.901,130,16:22:59.424,16:22:59.433,9,3661,16:22:59.433,0,505956,0,85327,,www.nbc.com - 2600:1406:5800::173c:a813:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2933,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54954,map.mp.nbc.com - 151.101.2.49:443,16:22:55.767,16:22:55.767,0,16:22:55.793,16:22:55.793,0,26,16:22:55.793,1470,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2932,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54956,map.mp.nbc.com - 151.101.2.49:443,16:22:55.767,16:22:55.767,0,16:22:55.793,16:22:55.793,0,26,16:22:55.793,174,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2930,https://www.nbc.com/generetic/generated/chunks/5521.e476de954d0bf8b88355.js,www.nbc.com,/generetic/generated/chunks/5521.e476de954d0bf8b88355.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:55.766,16:22:55.766,0,16:22:55.847,16:22:55.877,31,111,16:22:55.877,0,579576,0,89388,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2929,https://www.nbc.com/generetic/generated/chunks/4347.3ace46faec5171929283.js,www.nbc.com,/generetic/generated/chunks/4347.3ace46faec5171929283.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:55.766,16:22:55.766,0,16:22:56.054,16:22:56.055,1,289,16:22:56.055,0,56309,0,12171,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2928,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54955,map.mp.nbc.com - 151.101.2.49:443,16:22:55.766,16:22:55.766,0,16:22:55.793,16:22:55.793,0,27,16:22:55.793,1372,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2927,https://www.nbc.com/generetic/generated/chunks/379.ab56f6b122f60be849d9.js,www.nbc.com,/generetic/generated/chunks/379.ab56f6b122f60be849d9.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:55.766,16:22:55.766,0,16:22:55.849,16:22:55.867,18,101,16:22:55.867,0,2896717,0,416820,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2925,https://img.nbc.com/files/12014353_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014353_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54952,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.577,16:22:54.577,0,16:22:54.686,16:22:54.686,0,109,16:22:54.686,0,16933,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2924,https://img.nbc.com/files/12014311_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014311_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54961,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.456,16:22:54.547,91,16:22:54.609,16:22:54.609,0,153,16:22:54.609,0,15204,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2923,https://img.nbc.com/files/12014268_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014268_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54960,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.456,16:22:54.545,89,16:22:54.588,16:22:54.591,3,135,16:22:54.591,0,16513,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2922,https://img.nbc.com/files/12014268_1920x1080.jpg?impolicy=nbc_com&imwidth=1920&imdensity=1,img.nbc.com,/files/12014268_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54959,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.456,16:22:54.543,87,16:22:54.686,16:22:54.703,18,248,16:22:54.703,0,81691,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2924,https://img.nbc.com/files/12014222_1920x1080.jpg?impolicy=nbc_com&imwidth=1920&imdensity=1,img.nbc.com,/files/12014222_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54957,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.453,16:22:54.549,96,16:22:54.686,16:22:54.703,18,250,16:22:54.703,0,90358,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2923,https://img.nbc.com/files/12014273_1920x1080.jpg?impolicy=nbc_com&imwidth=1920&imdensity=1,img.nbc.com,/files/12014273_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54958,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.453,16:22:54.544,91,16:22:54.609,16:22:54.698,89,245,16:22:54.698,0,78235,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2919,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54955,map.mp.nbc.com - 151.101.2.49:443,16:22:54.449,16:22:54.547,98,16:22:54.578,16:22:54.578,0,129,16:22:54.578,7888,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2918,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54954,map.mp.nbc.com - 151.101.2.49:443,16:22:54.448,16:22:54.541,93,16:22:54.574,16:22:54.574,0,126,16:22:54.574,2105,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2917,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54956,map.mp.nbc.com - 151.101.2.49:443,16:22:54.448,16:22:54.547,100,16:22:54.576,16:22:54.576,0,128,16:22:54.576,2151,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2916,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54953,map.mp.nbc.com - 151.101.2.49:443,16:22:54.448,16:22:54.541,94,16:22:54.569,16:22:54.569,0,121,16:22:54.569,1297,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2916,https://img.nbc.com/files/2025-05/mlb_league.png,img.nbc.com,/files/2025-05/mlb_league.png,Completed,200,GET,image/avif,Google Chrome,127.0.0.1:54952,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:54.447,16:22:54.541,94,16:22:54.576,16:22:54.576,0,129,16:22:54.576,0,2693,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2915,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:54.441,16:22:54.441,0,16:22:54.470,16:22:54.470,0,29,16:22:54.470,1054,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2914,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54915,map.mp.nbc.com - 151.101.2.49:443,16:22:54.441,16:22:54.441,0,16:22:54.468,16:22:54.468,0,27,16:22:54.468,1294,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2912,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999374439&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fsports%2Fleague%2Fmlb&c8=Sport%20Landing%20Page%20-%20MLB&c9=,sb.scorecardresearch.com,/b,Completed,204,GET,,Google Chrome,127.0.0.1:54909,sb.scorecardresearch.com - 99.84.215.5:443,16:22:54.439,16:22:54.440,0,16:22:54.492,16:22:54.492,0,53,16:22:54.492,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2911,https://www.nbc.com/generetic/images/icons/down-arrow-icon.png,www.nbc.com,/generetic/images/icons/down-arrow-icon.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:54.439,16:22:54.439,0,16:22:54.481,16:22:54.481,0,41,16:22:54.481,0,228,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2910,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Atrue%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22sports%2Fleague%2Fmlb%22%2C%22type%22%3A%22LANDING_PAGE%22%2C%22subType%22%3A%22sport%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22appVersion%22%3A1249000%2C%22queryName%22%3A%22page%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22a6e21da7a9882cfe8498724f9f91fda9b8ab477666209d594dffb1742f77ad1e%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54948,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:53.939,16:22:54.023,84,16:22:54.421,16:22:54.422,0,483,16:22:54.422,0,85718,0,9414,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2909,https://friendship.nbc.com/v3/graphql?variables=%7B%22appVersion%22%3A1249000%2C%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Atrue%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22sports%2Fleague%2Fmlb%22%2C%22type%22%3A%22LANDING_PAGE%22%2C%22subType%22%3A%22sport%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22queryName%22%3A%22featuredSection%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22a76f2966d3fbe90881952db8e3b69f211d84c9c3d2d1d7cd9368378ece8e930e%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54947,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:53.939,16:22:54.024,85,16:22:54.078,16:22:54.078,0,139,16:22:54.078,0,32485,0,3676,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2908,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:53.648,16:22:53.648,0,16:22:53.679,16:22:53.679,0,31,16:22:53.679,173,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2907,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54915,map.mp.nbc.com - 151.101.2.49:443,16:22:53.648,16:22:53.648,0,16:22:53.692,16:22:53.692,0,44,16:22:53.692,971,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2906,https://www.nbc.com/generetic/generated/chunks/2050.ca8996e868f9976b39e4.js,www.nbc.com,/generetic/generated/chunks/2050.ca8996e868f9976b39e4.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:53.648,16:22:53.648,0,16:22:53.897,16:22:53.897,0,250,16:22:53.897,0,15824,0,3291,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2905,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54914,map.mp.nbc.com - 151.101.2.49:443,16:22:53.648,16:22:53.648,0,16:22:53.678,16:22:53.678,0,31,16:22:53.678,927,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2904,https://www.nbc.com/generetic/generated/chunks/7832.99917d345c3f54e967c4.js,www.nbc.com,/generetic/generated/chunks/7832.99917d345c3f54e967c4.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:53.648,16:22:53.648,0,16:22:53.920,16:22:53.920,0,273,16:22:53.920,0,61689,0,11339,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2903,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54914,map.mp.nbc.com - 151.101.2.49:443,16:22:52.974,16:22:52.974,0,16:22:53.026,16:22:53.026,0,52,16:22:53.026,1404,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2902,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:52.973,16:22:52.973,0,16:22:53.025,16:22:53.025,0,52,16:22:53.025,1267,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2900,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:49.890,16:22:49.890,0,16:22:49.968,16:22:49.968,0,79,16:22:49.968,4879,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2895,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:47.443,16:22:47.443,0,16:22:47.472,16:22:47.472,0,30,16:22:47.472,965,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2894,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54914,map.mp.nbc.com - 151.101.2.49:443,16:22:47.442,16:22:47.443,0,16:22:47.472,16:22:47.472,0,30,16:22:47.472,1275,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2893,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54915,map.mp.nbc.com - 151.101.2.49:443,16:22:47.440,16:22:47.441,0,16:22:47.469,16:22:47.469,0,28,16:22:47.469,1269,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2892,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54913,map.mp.nbc.com - 151.101.2.49:443,16:22:47.317,16:22:47.417,100,16:22:47.451,16:22:47.451,0,134,16:22:47.451,11067,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2891,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54915,map.mp.nbc.com - 151.101.2.49:443,16:22:47.317,16:22:47.409,92,16:22:47.440,16:22:47.440,0,123,16:22:47.440,6836,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2890,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54912,map.mp.nbc.com - 151.101.2.49:443,16:22:47.317,16:22:47.412,95,16:22:47.453,16:22:47.453,0,136,16:22:47.453,6882,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2889,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54914,map.mp.nbc.com - 151.101.2.49:443,16:22:47.317,16:22:47.412,95,16:22:47.442,16:22:47.442,0,125,16:22:47.442,1280,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2888,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54911,map.mp.nbc.com - 151.101.2.49:443,16:22:47.316,16:22:47.412,96,16:22:47.442,16:22:47.442,0,126,16:22:47.442,1048,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2892,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54910,map.mp.nbc.com - 151.101.2.49:443,16:22:47.313,16:22:47.414,101,16:22:47.446,16:22:47.446,0,133,16:22:47.446,1278,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2891,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999367305&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fsports&c8=NBC%20Sports%20Video%2C%20News%2C%20and%20Highlights%20%E2%80%93%20nbc.com&c9=,sb.scorecardresearch.com,/b,Completed,204,GET,,Google Chrome,127.0.0.1:54909,sb.scorecardresearch.com - 99.84.215.5:443,16:22:47.312,16:22:47.390,78,16:22:47.422,16:22:47.422,0,110,16:22:47.422,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2890,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15175960719290?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A47%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=global%3Ahome&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&contenthub=Adobe%20Pass&network=NBC%20Entertainment&title=global%3Ahome&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&nbcu.&contentGroup=Online&contentType=Home&showSite=Global&.nbcu&pageTitle=Home&.c&cc=USD&server=www.nbc.com&aamb=RKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y&c.&a.&activitymap.&page=nbcentertainment%3APC%3AMVPD%20Picker&link=SKIP®ion=main&pageIDType=1&.activitymap&.a&.c&pid=nbcentertainment%3APC%3AMVPD%20Picker&pidt=1&oid=functionjf%28%29%7B%7D&oidt=2&ot=BUTTON&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=157&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15175960719290",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54908,nbcume.sc.omtrdc.net - 63.140.37.201:443,16:22:47.312,16:22:47.446,135,16:22:47.499,16:22:47.499,0,187,16:22:47.499,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.201:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2876,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54864,sp.auth.adobe.com - 44.249.253.27:443,16:22:46.035,16:22:46.035,0,16:22:46.087,16:22:46.087,0,52,16:22:46.087,2270,515,0,435,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2874,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54881,map.mp.nbc.com - 151.101.2.49:443,16:22:45.960,16:22:46.038,78,16:22:46.074,16:22:46.074,0,114,16:22:46.074,4143,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2873,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54880,map.mp.nbc.com - 151.101.2.49:443,16:22:45.960,16:22:46.036,77,16:22:46.066,16:22:46.066,0,106,16:22:46.066,1300,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2873,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999365951&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fprovider-linked&c8=TV%20Provider%20Linked&c9=,sb.scorecardresearch.com,/b,Completed,204,GET,,Google Chrome,127.0.0.1:54877,sb.scorecardresearch.com - 99.84.215.5:443,16:22:45.959,16:22:46.016,57,16:22:46.045,16:22:46.045,0,86,16:22:46.045,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2872,https://www.nbc.com/generetic/images/authhero/identity_default_hero_bg.png?impolicy=nbc_com&imwidth=1920&imdensity=1,www.nbc.com,/generetic/images/authhero/identity_default_hero_bg.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54876,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:45.959,16:22:46.028,70,16:22:46.156,16:22:46.298,142,340,16:22:46.298,0,1190346,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2873,https://www.nbc.com/generetic/images/authhero/nbc-logo-white.png?impolicy=nbc_com&imwidth=340&imdensity=1,www.nbc.com,/generetic/images/authhero/nbc-logo-white.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54875,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:45.955,16:22:46.024,69,16:22:46.141,16:22:46.141,0,186,16:22:46.141,0,8031,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2870,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54848,map.mp.nbc.com - 151.101.2.49:443,16:22:45.954,16:22:45.954,0,16:22:45.987,16:22:45.987,0,33,16:22:45.987,1059,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2867,https://mps.nbcuni.com/request/page/json/params/?CALLBACK=mpsCallback&site=nbc-web&path=%2Fprovider-linked&LOADMODE=more&ASYNC=1&_=2&NOLOAD=mpstools&USE_OVERLAY=0&IRSOURCE=false,mps.nbcuni.com,/request/page/json/params/,Completed,200,GET,application/json; charset=utf-8,Google Chrome,127.0.0.1:54874,mps.nbcuni.com - 23.192.167.152:443,16:22:45.953,16:22:46.021,67,16:22:46.096,16:22:46.098,2,145,16:22:46.098,0,37199,0,9976,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2866,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54846,map.mp.nbc.com - 151.101.2.49:443,16:22:45.953,16:22:45.953,0,16:22:45.987,16:22:45.987,0,33,16:22:45.987,1298,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2865,https://www.nbc.com/generetic/images/authhero/brand-logos-1-liner@2x.png,www.nbc.com,/generetic/images/authhero/brand-logos-1-liner@2x.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:45.952,16:22:45.952,0,16:22:46.000,16:22:46.000,0,48,16:22:46.000,0,10444,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2863,https://sp.auth.adobe.com/adobe-services/preauthorize,sp.auth.adobe.com,/adobe-services/preauthorize,Completed,200,POST,application/xml;charset=UTF-8,Google Chrome,127.0.0.1:54867,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.881,16:22:45.983,102,16:22:46.606,16:22:46.606,0,725,16:22:46.606,2157,1372,0,244,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2863,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54865,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.880,16:22:45.985,106,16:22:46.045,16:22:46.045,0,165,16:22:46.045,2270,515,0,435,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2862,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54866,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.880,16:22:45.984,104,16:22:46.038,16:22:46.038,0,159,16:22:46.038,2270,515,0,434,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2862,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54864,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.877,16:22:45.977,100,16:22:46.034,16:22:46.034,0,157,16:22:46.034,2270,515,0,434,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2861,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54863,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.877,16:22:45.983,106,16:22:46.038,16:22:46.038,0,162,16:22:46.038,2270,515,0,431,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2860,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54862,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.874,16:22:45.979,105,16:22:46.034,16:22:46.034,0,160,16:22:46.034,2270,515,0,434,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2857,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54843,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.871,16:22:45.871,0,16:22:45.909,16:22:45.909,0,38,16:22:45.909,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2856,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54840,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.666,16:22:45.833,166,16:22:45.874,16:22:45.874,0,207,16:22:45.874,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2855,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54843,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.666,16:22:45.830,163,16:22:45.869,16:22:45.869,0,203,16:22:45.869,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2854,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54841,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.666,16:22:45.831,165,16:22:45.871,16:22:45.871,0,205,16:22:45.871,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2853,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54848,map.mp.nbc.com - 151.101.2.49:443,16:22:45.666,16:22:45.821,155,16:22:45.850,16:22:45.850,0,184,16:22:45.850,991,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2852,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54845,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.666,16:22:45.830,165,16:22:45.871,16:22:45.871,0,206,16:22:45.871,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2851,https://ss.nbc.co/conveyor/search?mpid=-8189650932773340651,ss.nbc.co,/conveyor/search,Error,404,GET,application/json,Google Chrome,127.0.0.1:54849,ss.nbc.co - 2600:1406:4e00:19::1738:6d4c:443,16:22:45.666,16:22:45.807,141,16:22:46.030,16:22:46.030,0,364,16:22:46.030,0,132,0,0,,ss.nbc.co - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.co,*.nbc.co; nbc.co; ss.nbc.co,
|
||||
2850,https://sp.auth.adobe.com/adobe-services/usermetadata,sp.auth.adobe.com,/adobe-services/usermetadata,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54844,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.666,16:22:45.832,166,16:22:45.874,16:22:45.874,0,208,16:22:45.874,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2849,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54846,map.mp.nbc.com - 151.101.2.49:443,16:22:45.666,16:22:45.826,160,16:22:45.855,16:22:45.855,0,189,16:22:45.855,961,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2848,https://sp.auth.adobe.com/adobe-services/preauthorize,sp.auth.adobe.com,/adobe-services/preauthorize,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54842,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.665,16:22:45.833,168,16:22:45.877,16:22:45.877,0,212,16:22:45.877,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2856,https://www.nbc.com/generetic/generated/chunks/7388.82469fe00df132b4f19b.js,www.nbc.com,/generetic/generated/chunks/7388.82469fe00df132b4f19b.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54838,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:45.656,16:22:45.776,119,16:22:45.941,16:22:45.941,0,285,16:22:45.941,0,17306,0,3798,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2844,https://api.nbc.com/localized-mvpd/entitlements?mvpd=Verizon&platform=iOS2&brand=nbcentertainment&instance=prod,api.nbc.com,/localized-mvpd/entitlements,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54833,api.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:45.488,16:22:45.558,70,16:22:45.640,16:22:45.640,0,152,16:22:45.640,0,24201,0,2960,,api.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; api.nbc.com,
|
||||
2843,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s17566515395113?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A45%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=nbcentertainment%3APC%3AMVPD%20Picker&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&passmvpd=Verizon&passguid=1d547c24870d5152823ebb0c5ca5f837&contenthub=Adobe%20Pass&passnetwork=nbcentertainment&passauthensuccess=true&passauthen=Authenticated&title=nbcentertainment%3APC%3AMVPD%20Picker&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&.c&cc=USD&pe=lnk_o&pev2=Adobe%20Pass%3AAuthenticate%3ASuccess&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=172&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s17566515395113",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54832,nbcume.sc.omtrdc.net - 63.140.37.172:443,16:22:45.483,16:22:45.583,101,16:22:45.635,16:22:45.635,0,152,16:22:45.635,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.172:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2842,https://sp.auth.adobe.com/adobe-services/session,sp.auth.adobe.com,/adobe-services/session,Completed,200,POST,application/xml;charset=ISO-8859-1,Google Chrome,127.0.0.1:54830,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.255,16:22:45.353,98,16:22:45.471,16:22:45.471,0,216,16:22:45.471,58,2160,0,1212,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2841,https://sp.auth.adobe.com/adobe-services/session,sp.auth.adobe.com,/adobe-services/session,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54826,sp.auth.adobe.com - 44.249.253.27:443,16:22:45.059,16:22:45.162,102,16:22:45.246,16:22:45.246,0,187,16:22:45.246,0,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2839,https://entitlement.auth.adobe.com/entitlement/v4/completeBackgroundLogin.html,entitlement.auth.adobe.com,/entitlement/v4/completeBackgroundLogin.html,Completed,200,GET,text/html,Google Chrome,127.0.0.1:54824,entitlement.auth.adobe.com - 23.42.82.205:443,16:22:44.942,16:22:45.013,71,16:22:45.029,16:22:45.029,0,87,16:22:45.029,0,238,0,168,,entitlement.auth.adobe.com - 23.42.82.205:443,ssl.adobe.com,ssl.adobe.com; signcsa.experienceleague.stage.adobe.com; wwwimages.stage2.adobe.com; www.macromedia.com; www.adobeidealab.com; cdn-stg-ffc.oobesaas.adobe.com; solutionpartners.stage2.adobe.com; www-stage01.acrobat.adobe.com; updates.adobeleanprint.com; download.stage.adobeprerelease.com; store2.stage2.adobe.com; live.adobeprimetime.com; wwwimages2.stage2.adobe.com; adobeprerelease.com; exchange.adobe.com; stage.status.adobe.com; experiencecloud.adobeexchange.com; www.stage.macromedia.com; experiencecloudstg2.adobeexchange.com; www.stage.adobeprerelease.com; www.qa.adobeprerelease.com; fpdownload.macromedia.com; stage.adobeprerelease.com; plan.adobe.com; dev.status.adobe.com; static.stage.adobeprimetime.com; preprod.status.adobe.com; live.stage.adobeprimetime.com; www.adobe.io; qe-ffc-static-cdn.oobesaas.adobe.com; download.adobeprerelease.com; trainingpartners.stage2.adobe.com; www.adobe-students.com; store1.stage2.adobe.com; auth.adobefpl.com; partners.stage2.adobe.com; media.stage.adobeprimetime.com; stage.adobe.io; cdn-qe-ffc.oobesaas.adobe.com; geo2.adobe.com; media.adobeprimetime.com; ffc-static-cdn.oobesaas.adobe.com; data.status.adobe.com; updates.stage.adobeleanprint.com; static.adobeprimetime.com; www.adobeprerelease.com; signcsa.experienceleague.adobe.com; hendrix360.qe.adobe.com; download.macromedia.com; csa.experienceleague.stage.adobe.com; app-cdn.stage2.adobe.com; stage.plan.adobe.com; www.stage2.adobe.com; stage.exchange.adobe.com; csa.experienceleague.adobe.com; entitlement.auth.adobe.com; download.qa.adobeprerelease.com; channelpartners.stage2.adobe.com; cdn-ffc.oobesaas.adobe.com; stg-ffc-static-cdn.oobesaas.adobe.com; technologypartners.stage2.adobe.com; www.stage.adobe-students.com,
|
||||
2838,https://sp.auth.adobe.com/sp/saml/SAMLAssertionConsumer,sp.auth.adobe.com,/sp/saml/SAMLAssertionConsumer,Redirect,302,POST,,Google Chrome,127.0.0.1:54822,sp.auth.adobe.com - 44.249.253.27:443,16:22:44.589,16:22:44.804,215,16:22:44.934,16:22:44.934,0,345,16:22:44.934,8228,0,0,0,,sp.auth.adobe.com - 44.249.253.27:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2836,https://sanalytics.verizon.com/ee/or2/v1/collect?configId=60e576ec-f42a-48ad-9fe5-0b9a866a6413&requestId=d14227dc-a932-46bc-b8b4-cfd51fe41efe,sanalytics.verizon.com,/ee/or2/v1/collect,Completed,204,POST,,Google Chrome,127.0.0.1:54815,sanalytics.verizon.com - 63.140.36.154:443,16:22:44.041,16:22:44.247,206,16:22:44.325,16:22:44.325,0,284,16:22:44.325,4947,0,0,0,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2835,https://passwordsleakcheck-pa.googleapis.com/v1/leaks:lookupSingle,passwordsleakcheck-pa.googleapis.com,/v1/leaks:lookupSingle,Completed,200,POST,application/x-protobuf,Google Chrome,127.0.0.1:54811,passwordsleakcheck-pa.googleapis.com - 2607:f8b0:4007:80b::200a:443,16:22:43.892,16:22:43.962,70,16:22:44.039,16:22:44.040,0,148,16:22:44.040,45,3523,0,3449,,passwordsleakcheck-pa.googleapis.com - 2607:f8b0:4007:80b::200a:443,upload.video.google.com,upload.video.google.com; *.clients.google.com; *.docs.google.com; *.drive.google.com; *.gdata.youtube.com; *.googleapis.com; *.photos.google.com; *.youtube-3rd-party.com; upload.google.com; *.upload.google.com; upload.youtube.com; *.upload.youtube.com; uploads.stage.gdata.youtube.com; bg-call-donation.goog; bg-call-donation-alpha.goog; bg-call-donation-canary.goog; bg-call-donation-dev.goog; passwordsleakcheck-pa.googleapis.com,
|
||||
2830,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCbee5e1d24157460e8d62373d628e49fe-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCbee5e1d24157460e8d62373d628e49fe-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54784,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:43.582,16:22:43.582,0,16:22:43.674,16:22:43.675,0,93,16:22:43.675,0,554,0,336,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2828,https://sanalytics.verizon.com/ee/or2/v1/interact?configId=028f67d9-9bd3-4306-b399-0c5025453572&requestId=f7ca6a4f-908f-4dc5-802d-ef6b9882f85b,sanalytics.verizon.com,/ee/or2/v1/interact,Completed,200,POST,application/json;charset=utf-8,Google Chrome,127.0.0.1:54783,sanalytics.verizon.com - 63.140.36.154:443,16:22:43.512,16:22:43.512,0,16:22:43.579,16:22:43.579,0,67,16:22:43.579,3367,442,0,283,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2827,https://ssoauth.verizon.com/sso/TVPFR7HandlerServlet?loginType=vzRedirect&partner=nbcentertainment&partnerlogo=null&RelayState=34d6b7dc-f921-4ac5-964c-291f96ed842a&cancelURL=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2Fsession%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&TARGET=https%3A%2F%2Fsp.auth.adobe.com%2Fsp%2Fsaml%2FSAMLAssertionConsumer&hl=no,ssoauth.verizon.com,/sso/TVPFR7HandlerServlet,Completed,200,POST,,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:43.485,16:22:43.485,0,16:22:44.571,16:22:44.571,0,1085,16:22:44.571,0,8119,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2825,https://ssoauth.verizon.com/sso/choice/tvpFR7Handler.jsp?loginType=vzRedirect&partner=nbcentertainment&partnerlogo=null&RelayState=34d6b7dc-f921-4ac5-964c-291f96ed842a&cancelURL=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2Fsession%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&TARGET=https%3A%2F%2Fsp.auth.adobe.com%2Fsp%2Fsaml%2FSAMLAssertionConsumer&hl=no,ssoauth.verizon.com,/sso/choice/tvpFR7Handler.jsp,Completed,200,GET,text/html; charset=ISO-8859-1,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:43.152,16:22:43.152,0,16:22:43.461,16:22:43.461,0,309,16:22:43.461,0,8816,0,2526,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2823,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:42.892,16:22:42.892,0,16:22:43.151,16:22:43.151,0,258,16:22:43.151,5860,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2820,https://ssoauth.verizon.com/sso/TVPFR7HandlerServlet?goto=https://ssoauth.verizon.com/sso/choice/tvpFR7Handler.jsp?loginType%3DvzRedirect%26partner%3Dnbcentertainment%26partnerlogo%3Dnull%26RelayState%3D34d6b7dc-f921-4ac5-964c-291f96ed842a%26cancelURL%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fadobe-services%252F1.0%252Fsession%253Fcancelled%253D1%2526_method%253DPOST%2526mso_id%253DVerizon%26TARGET%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fsp%252Fsaml%252FSAMLAssertionConsumer%26hl%3Dno&clientId=TvLogin&partner=nbcentertainment&errorURL=https://ssoauth.verizon.com/sso/VOLPortalLogin?src%3DSAM%26loginType%3DvzRedirect%26partner%3Dnbcentertainment%26partnerlogo%3Dnull%26RelayState%3D34d6b7dc-f921-4ac5-964c-291f96ed842a%26cancelURL%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fadobe-services%252F1.0%252Fsession%253Fcancelled%253D1%2526_method%253DPOST%2526mso_id%253DVerizon%26TARGET%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fsp%252Fsaml%252FSAMLAssertionConsumer%26hl%3Dno,ssoauth.verizon.com,/sso/TVPFR7HandlerServlet,Internalerror,999,POST,,Google Chrome,127.0.0.1:54771,ssoauth.verizon.com - 23.217.118.25:443,16:22:41.952,16:22:41.952,0,16:22:43.149,16:22:43.150,1,1198,16:22:43.150,1336,0,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2819,https://sanalytics.verizon.com/ee/or2/v1/collect?configId=60e576ec-f42a-48ad-9fe5-0b9a866a6413&requestId=06859c18-94e0-4402-8295-f9b62236bd23,sanalytics.verizon.com,/ee/or2/v1/collect,Completed,204,POST,,Google Chrome,127.0.0.1:54783,sanalytics.verizon.com - 63.140.36.154:443,16:22:41.946,16:22:41.947,0,16:22:42.017,16:22:42.017,0,71,16:22:42.017,8330,0,0,0,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2817,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:41.879,16:22:41.879,0,16:22:42.242,16:22:42.242,0,364,16:22:42.242,3871,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2816,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:41.148,16:22:41.148,0,16:22:41.436,16:22:41.436,0,289,16:22:41.436,6902,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2815,https://sanalytics.verizon.com/ee/or2/v1/collect?configId=60e576ec-f42a-48ad-9fe5-0b9a866a6413&requestId=f7a6f962-ee96-48d0-8242-f5dd2b9f6396,sanalytics.verizon.com,/ee/or2/v1/collect,Completed,204,POST,,Google Chrome,127.0.0.1:54783,sanalytics.verizon.com - 63.140.36.154:443,16:22:41.062,16:22:41.172,109,16:22:41.282,16:22:41.282,0,219,16:22:41.282,8330,0,0,0,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2814,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCace0285ad6cb44ca9e86b2a1a315fa7c-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCace0285ad6cb44ca9e86b2a1a315fa7c-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54784,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:41.062,16:22:41.111,49,16:22:41.136,16:22:41.136,0,74,16:22:41.136,0,1277,0,410,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2813,https://ssoauth.verizon.com/sso/TVPFR7HandlerServlet?goto=https://ssoauth.verizon.com/sso/choice/tvpFR7Handler.jsp?loginType%3DvzRedirect%26partner%3Dnbcentertainment%26partnerlogo%3Dnull%26RelayState%3D34d6b7dc-f921-4ac5-964c-291f96ed842a%26cancelURL%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fadobe-services%252F1.0%252Fsession%253Fcancelled%253D1%2526_method%253DPOST%2526mso_id%253DVerizon%26TARGET%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fsp%252Fsaml%252FSAMLAssertionConsumer%26hl%3Dno&clientId=TvLogin&partner=nbcentertainment&errorURL=https://ssoauth.verizon.com/sso/VOLPortalLogin?src%3DSAM%26loginType%3DvzRedirect%26partner%3Dnbcentertainment%26partnerlogo%3Dnull%26RelayState%3D34d6b7dc-f921-4ac5-964c-291f96ed842a%26cancelURL%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fadobe-services%252F1.0%252Fsession%253Fcancelled%253D1%2526_method%253DPOST%2526mso_id%253DVerizon%26TARGET%3Dhttps%253A%252F%252Fsp.auth.adobe.com%252Fsp%252Fsaml%252FSAMLAssertionConsumer%26hl%3Dno,ssoauth.verizon.com,/sso/TVPFR7HandlerServlet,Internalerror,999,POST,,Google Chrome,127.0.0.1:54765,ssoauth.verizon.com - 23.217.118.25:443,16:22:41.056,16:22:41.057,0,16:22:41.942,16:22:41.942,0,885,16:22:41.942,1336,0,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2810,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCe6c8547fbdd44141a0acb30d0af789b5-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCe6c8547fbdd44141a0acb30d0af789b5-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54777,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:40.906,16:22:40.965,59,16:22:41.054,16:22:41.054,0,149,16:22:41.054,0,743,0,375,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2809,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54774,ssoauth.verizon.com - 23.217.118.25:443,16:22:40.897,16:22:40.952,54,16:22:41.158,16:22:41.158,0,261,16:22:41.158,7037,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2808,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54773,ssoauth.verizon.com - 23.217.118.25:443,16:22:40.897,16:22:40.966,69,16:22:41.249,16:22:41.249,0,352,16:22:41.249,6938,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2807,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54771,ssoauth.verizon.com - 23.217.118.25:443,16:22:40.883,16:22:40.951,68,16:22:41.394,16:22:41.394,0,511,16:22:41.394,5860,17,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2806,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:40.876,16:22:40.876,0,16:22:41.147,16:22:41.147,0,271,16:22:41.147,7086,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2805,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54765,ssoauth.verizon.com - 23.217.118.25:443,16:22:40.872,16:22:40.872,0,16:22:41.056,16:22:41.056,0,184,16:22:41.056,6998,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2804,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:40.868,16:22:40.869,0,16:22:41.269,16:22:41.269,0,401,16:22:41.269,7116,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2803,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:40.224,16:22:40.224,0,16:22:40.428,16:22:40.428,0,204,16:22:40.428,6639,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2802,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54767,map.mp.nbc.com - 151.101.2.49:443,16:22:39.787,16:22:39.851,64,16:22:39.883,16:22:39.883,0,96,16:22:39.883,4353,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2801,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:39.728,16:22:39.728,0,16:22:40.006,16:22:40.007,0,278,16:22:40.007,6453,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2800,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54765,ssoauth.verizon.com - 23.217.118.25:443,16:22:39.682,16:22:39.743,61,16:22:40.004,16:22:40.004,0,323,16:22:40.004,6541,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2799,https://sanalytics.verizon.com/ee/or2/v1/collect?configId=60e576ec-f42a-48ad-9fe5-0b9a866a6413&requestId=f6a691ea-240d-4ee9-be4a-9e29371748ef,sanalytics.verizon.com,/ee/or2/v1/collect,Completed,204,POST,,Google Chrome,127.0.0.1:54722,sanalytics.verizon.com - 63.140.36.154:443,16:22:39.498,16:22:39.499,0,16:22:39.561,16:22:39.561,0,63,16:22:39.561,8492,0,0,0,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2798,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC97da83d389df405fb9a8e93f6ad74e96-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC97da83d389df405fb9a8e93f6ad74e96-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54756,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:39.498,16:22:39.499,0,16:22:39.519,16:22:39.519,0,20,16:22:39.519,0,1584,0,455,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2797,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:39.476,16:22:39.476,0,16:22:39.701,16:22:39.701,0,225,16:22:39.701,6330,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2796,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC568d08de522a4f72a90dd358b252e3d3-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC568d08de522a4f72a90dd358b252e3d3-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54756,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:39.466,16:22:39.466,0,16:22:39.492,16:22:39.492,0,26,16:22:39.492,0,596,0,320,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2795,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54762,ssoauth.verizon.com - 23.217.118.25:443,16:22:39.167,16:22:39.231,64,16:22:39.697,16:22:39.697,0,530,16:22:39.697,6313,18,0,0,,ssoauth.verizon.com - 23.217.118.25:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2794,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:39.128,16:22:39.128,0,16:22:39.324,16:22:39.324,0,196,16:22:39.324,6135,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2792,https://www.verizon.com/business/geoloc.json,www.verizon.com,/business/geoloc.json,Completed,200,GET,text/javascript; charset=utf-8,Google Chrome,127.0.0.1:54758,www.verizon.com - 23.217.118.33:443,16:22:38.794,16:22:38.855,61,16:22:38.954,16:22:38.954,0,161,16:22:38.954,0,148,0,0,,www.verizon.com - 23.217.118.33:443,verizon.com,verizon.com; ws01.static-verizon.com; ws02.static-verizon.com; ws03.static-verizon.com; ws04.static-verizon.com; www.verizon.com,
|
||||
2791,https://ssoauth.verizon.com/favicon.ico,ssoauth.verizon.com,/favicon.ico,Completed,200,GET,image/x-icon,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:38.779,16:22:38.780,0,16:22:38.954,16:22:38.954,0,175,16:22:38.954,0,822,0,94,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2790,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC7484a41e5cdc40acb9076fc220290294-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RC7484a41e5cdc40acb9076fc220290294-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54756,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:38.730,16:22:38.730,0,16:22:38.748,16:22:38.748,0,18,16:22:38.748,0,524,0,303,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2789,https://www.verizon.com/vendor/transcend/vz/xdi.js,www.verizon.com,/vendor/transcend/vz/xdi.js,Completed,200,GET,application/x-javascript; charset=utf-8,Google Chrome,127.0.0.1:54742,www.verizon.com - 23.217.118.33:443,16:22:38.713,16:22:38.713,0,16:22:38.765,16:22:38.765,0,52,16:22:38.765,0,26230,0,11782,,www.verizon.com - 23.217.118.33:443,verizon.com,verizon.com; ws01.static-verizon.com; ws02.static-verizon.com; ws03.static-verizon.com; ws04.static-verizon.com; www.verizon.com,
|
||||
2788,https://assets.adobedtm.com/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCd269e4a00d46491bbf41a20741d40442-source.min.js,assets.adobedtm.com,/2ea7ee22c8c2/fee1b09a7b1e/f5f782cfa86b/RCd269e4a00d46491bbf41a20741d40442-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54756,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:38.649,16:22:38.709,60,16:22:38.726,16:22:38.726,0,77,16:22:38.726,0,840,0,445,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2782,https://www.verizon.com/vendor/transcend/vz/xdi.html,www.verizon.com,/vendor/transcend/vz/xdi.html,Completed,200,GET,text/html; charset=utf-8,Google Chrome,127.0.0.1:54742,www.verizon.com - 23.217.118.33:443,16:22:38.513,16:22:38.610,96,16:22:38.708,16:22:38.708,0,195,16:22:38.708,0,2876,0,1224,,www.verizon.com - 23.217.118.33:443,verizon.com,verizon.com; ws01.static-verizon.com; ws02.static-verizon.com; ws03.static-verizon.com; ws04.static-verizon.com; www.verizon.com,
|
||||
2778,https://sanalytics.verizon.com/ee/or2/v1/interact?configId=028f67d9-9bd3-4306-b399-0c5025453572&requestId=1d66aa47-6418-4d41-9ad3-4fdd80aacdf7,sanalytics.verizon.com,/ee/or2/v1/interact,Completed,200,POST,application/json;charset=utf-8,Google Chrome,127.0.0.1:54722,sanalytics.verizon.com - 63.140.36.154:443,16:22:38.349,16:22:38.568,219,16:22:38.642,16:22:38.642,0,293,16:22:38.642,4542,442,0,280,,sanalytics.verizon.com - 63.140.36.154:443,sanalytics.verizon.com,sanalytics.verizon.com,
|
||||
2764,https://secure.verizon.com/vendor/transcend/vcg/xdi.html,secure.verizon.com,/vendor/transcend/vcg/xdi.html,Completed,200,GET,text/html; charset=utf-8,Google Chrome,127.0.0.1:54705,secure.verizon.com - 23.217.118.25:443,16:22:38.205,16:22:38.288,83,16:22:38.433,16:22:38.433,0,228,16:22:38.433,0,2803,0,1208,,secure.verizon.com - 23.217.118.25:443,login.verizonwireless.com,login.verizonwireless.com; tlogin.verizonwireless.com; securepp.verizon.com; secure.verizon.com,
|
||||
2762,https://adobedc.demdex.net/ee/v1/identity/acquire?configId=60e576ec-f42a-48ad-9fe5-0b9a866a6413&requestId=1f2588bb-3c94-458e-a81f-cc26b165db2b,adobedc.demdex.net,/ee/v1/identity/acquire,Completed,200,POST,application/json;charset=utf-8,Google Chrome,127.0.0.1:54699,adobedc.demdex.net - 63.140.37.142:443,16:22:38.124,16:22:38.258,134,16:22:38.314,16:22:38.315,1,191,16:22:38.315,140,2523,0,1078,,adobedc.demdex.net - 63.140.37.142:443,adobedc.demdex.net,adobedc.demdex.net,
|
||||
2758,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,201,POST,application/json,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:38.110,16:22:38.111,0,16:22:38.309,16:22:38.309,0,198,16:22:38.309,4074,18,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2756,https://assets.adobedtm.com/10d5272d092923c410feae744225087686012423/satelliteLib-8df7d93db820b272138ecb04dbe4ed7f5023b893.js,assets.adobedtm.com,/10d5272d092923c410feae744225087686012423/satelliteLib-8df7d93db820b272138ecb04dbe4ed7f5023b893.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54692,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:37.954,16:22:38.003,48,16:22:38.026,16:22:38.081,55,127,16:22:38.081,0,950253,0,230440,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2755,https://ssoauth.verizon.com/sso/VOLPortalLogin?hl=no&SAMLRequest=fVJdc9owEPwrGr37A7mFoMFkaJhMmaGNGzt56NvZHESMLbk6mX78%2BgpDXDrJ8Gaf9nZv9252%2B6up2QEtKaNTPgpjzlBXZqP0LuVPxX1ww2%2FnM4KmFq1cdO5FP%2BKPDskx36hJnl5S3lktDZAiqaFBkq6S%2BeLLWoowlq01zlSm5mxBhNZ5qTujqWvQ5mgPqsKnx3XKX5xrSUYRtSF4oRA2psSwMo2vREed6Mj4hoKzpR9HaXC9hYGFTM%2Fivak%2FRp94yETPD%2BvMWAf12uyU5uze2Ap7YynfQk3I2WqZctjDeAs4KZVS04%2Bl2IyEELtykuz3H%2BKJx1AGROqA%2F7qIOlxpcqBdykUsxkGcBMmoEIkUQibjcHoz%2Fc5Zdk7jk9KnlK9FV55AJD8XRRZkD3nB2fPrtjyAn3cje3V7uZTrxPAaI58Pifmu8E34s%2BhSYDiFr55xtcxMrarfbFHX5uedRXA%2BDmc77GNtwF2f4VhRm2DbQ6WzoEmhdpzl2ZH%2BWwe12iq0F0t9d0QeDWPllWl9XsP%2F%2BVjRrpZzXVae3bsGpRv%2FdXb2P2goDkzRO8c%2F%2Fws%3D&RelayState=34d6b7dc-f921-4ac5-964c-291f96ed842a&TARGET=https%3A%2F%2Fsp.auth.adobe.com%2Fsp%2Fsaml%2FSAMLAssertionConsumer&TOP=yes&cancelURL=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2Fsession%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&cancelURLDevice=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2FsessionDevice%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&loginType=vzRedirect&partner=nbcentertainment&scb=yes&ver=2&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=HClc34LA%2FAvIgyDTBP1AX9OlW1WshudAkANCyBS%2Blz6SVICCnqu0luqnxYPqyvCZxgxxhGv1l8EMaYXX1Qedrox4lwJbTO6fG4wCzFRQftxz22eDyhnd2zrTXuWF9Gjcf9uCOzsFXscjDgXVy%2FskC3AE3iACJGCqbMudFQ65jR3%2FUxG1ODLZ7C4VGBWM4zm%2B94J%2BObjPdxE3rS9VntK8HEWnU9ilc%2FQD3qgF8qhF5GUzqZik5Vv1AnvS5h6thpMdlF69Qbjwv2OG7HIN0Qfb4c%2BxvO2%2FlWgBgyeCPA9DVPAdqrAD8JY1ZZpjGDwfbSaQIokaS74txNzU7Pa6V6zOfQ%3D%3D,ssoauth.verizon.com,/sso/VOLPortalLogin,Completed,200,GET,text/html; charset=UTF-8,Google Chrome,127.0.0.1:54690,ssoauth.verizon.com - 23.217.118.26:443,16:22:37.693,16:22:37.759,66,16:22:37.932,16:22:37.932,0,238,16:22:37.932,0,25139,0,4739,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2754,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Internalerror,999,POST,,Google Chrome,127.0.0.1:54678,ssoauth.verizon.com - 23.217.118.26:443,16:22:37.645,16:22:37.645,0,16:22:37.934,16:22:37.934,0,289,16:22:37.934,3965,0,0,0,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2749,https://ssoauth.verizon.com/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,ssoauth.verizon.com,/B-vhP6NcScBpVMYp-mcRBOJFHos/hQ1NpSOEi5E9Xc5r/LGsGQUshZAg/chFK/SlEqbWsB,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54678,ssoauth.verizon.com - 23.217.118.26:443,16:22:37.318,16:22:37.318,0,16:22:37.450,16:22:37.478,28,161,16:22:37.478,0,533056,0,172655,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2748,https://ssoauth.verizon.com/sso/VOLPortalLogin?SAMLRequest=fVJdc9owEPwrGr37A7mFoMFkaJhMmaGNGzt56NvZHESMLbk6mX78%2BgpDXDrJ8Gaf9nZv9252%2B6up2QEtKaNTPgpjzlBXZqP0LuVPxX1ww2%2FnM4KmFq1cdO5FP%2BKPDskx36hJnl5S3lktDZAiqaFBkq6S%2BeLLWoowlq01zlSm5mxBhNZ5qTujqWvQ5mgPqsKnx3XKX5xrSUYRtSF4oRA2psSwMo2vREed6Mj4hoKzpR9HaXC9hYGFTM%2Fivak%2FRp94yETPD%2BvMWAf12uyU5uze2Ap7YynfQk3I2WqZctjDeAs4KZVS04%2Bl2IyEELtykuz3H%2BKJx1AGROqA%2F7qIOlxpcqBdykUsxkGcBMmoEIkUQibjcHoz%2Fc5Zdk7jk9KnlK9FV55AJD8XRRZkD3nB2fPrtjyAn3cje3V7uZTrxPAaI58Pifmu8E34s%2BhSYDiFr55xtcxMrarfbFHX5uedRXA%2BDmc77GNtwF2f4VhRm2DbQ6WzoEmhdpzl2ZH%2BWwe12iq0F0t9d0QeDWPllWl9XsP%2F%2BVjRrpZzXVae3bsGpRv%2FdXb2P2goDkzRO8c%2F%2Fws%3D&RelayState=34d6b7dc-f921-4ac5-964c-291f96ed842a&TARGET=https%3A%2F%2Fsp.auth.adobe.com%2Fsp%2Fsaml%2FSAMLAssertionConsumer&TOP=yes&cancelURL=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2Fsession%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&cancelURLDevice=https%3A%2F%2Fsp.auth.adobe.com%2Fadobe-services%2F1.0%2FsessionDevice%3Fcancelled%3D1%26_method%3DPOST%26mso_id%3DVerizon&loginType=vzRedirect&partner=nbcentertainment&scb=yes&ver=2&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=HClc34LA%2FAvIgyDTBP1AX9OlW1WshudAkANCyBS%2Blz6SVICCnqu0luqnxYPqyvCZxgxxhGv1l8EMaYXX1Qedrox4lwJbTO6fG4wCzFRQftxz22eDyhnd2zrTXuWF9Gjcf9uCOzsFXscjDgXVy%2FskC3AE3iACJGCqbMudFQ65jR3%2FUxG1ODLZ7C4VGBWM4zm%2B94J%2BObjPdxE3rS9VntK8HEWnU9ilc%2FQD3qgF8qhF5GUzqZik5Vv1AnvS5h6thpMdlF69Qbjwv2OG7HIN0Qfb4c%2BxvO2%2FlWgBgyeCPA9DVPAdqrAD8JY1ZZpjGDwfbSaQIokaS74txNzU7Pa6V6zOfQ%3D%3D,ssoauth.verizon.com,/sso/VOLPortalLogin,Completed,200,GET,text/html; charset=UTF-8,Google Chrome,127.0.0.1:54678,ssoauth.verizon.com - 23.217.118.26:443,16:22:37.016,16:22:37.105,89,16:22:37.302,16:22:37.303,0,287,16:22:37.303,0,4288,0,2133,,ssoauth.verizon.com - 23.217.118.26:443,ssoauth.verizon.com,ssoauth.verizon.com; pprod-ssoauth.verizon.com; pprd-ssoauth.verizon.com,
|
||||
2747,https://sp.auth.adobe.com/adobe-services/authenticate/saml?reg_code=JX47BH7&mso_id=Verizon&requestor_id=nbcentertainment&no_iframe=true&domain_name=adobe.com&redirect_url=https%3A%2F%2Fentitlement.auth.adobe.com%2Fentitlement%2Fv4%2FcompleteBackgroundLogin.html,sp.auth.adobe.com,/adobe-services/authenticate/saml,Redirect,302,GET,,Google Chrome,127.0.0.1:54676,sp.auth.adobe.com - 35.161.249.167:443,16:22:36.791,16:22:36.951,160,16:22:37.006,16:22:37.006,0,215,16:22:37.006,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2746,https://sb.scorecardresearch.com/p?c1=19&c2=6035083&ns_ap_an=unknown&ns_ap_pn=js&ns_ap_pv=5&c12=_&name=foreground&ns_ap_ec=2&ns_ap_ev=hidden&ns_ap_device=MacIntel&ns_ap_id=1774999355781&ns_ap_bi=unknown&ns_ap_pfm=webbrowser&ns_ap_pfv=Chrome%20146.0.0.0&ns_ap_ver=unknown&ns_ap_sv=7.6.0.210114&ns_ap_bv=7.6.0.210114&ns_ap_smv=6.4&ns_type=hidden&ns_ts=1774999350619&ns_ap_env=0-0-2&ns_ap_ut=60000&ns_ap_ar=unknown&ns_ap_cs=1&ns_ap_fg=0&ns_ap_dft=0&ns_ap_dbt=0&ns_ap_dit=0&ns_ap_as=0&ns_ap_das=0&ns_ap_usage=0&ns_radio=unknown&cs_ucfr=&ns_ap_ft=0&ns_ap_bt=0&ns_ap_it=0&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_po=0x0&ns_ap_lang=en-US&ns_c=UTF-8&c7=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&c8=MVPD%20Picker&c9=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:54670,sb.scorecardresearch.com - 99.84.215.5:443,16:22:35.790,16:22:35.860,70,16:22:35.892,16:22:35.892,0,101,16:22:35.892,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2745,https://sb.scorecardresearch.com/p?c1=19&c2=6035083&ns_ap_an=unknown&ns_ap_pn=js&ns_ap_pv=5&c12=_&name=foreground&ns_ap_ec=1&ns_ap_ev=start&ns_ap_device=MacIntel&ns_ap_id=1774999355781&ns_ap_csf=1&ns_ap_bi=unknown&ns_ap_pfm=webbrowser&ns_ap_pfv=Chrome%20146.0.0.0&ns_ap_ver=unknown&ns_ap_sv=7.6.0.210114&ns_ap_bv=7.6.0.210114&ns_ap_smv=6.4&ns_type=view&ns_ap_gs=1774999350619&ns_ts=1774999350619&ns_ap_cfg=1110101-110-3C-7D0-A-1F-1E-1E-12C-A&ns_ap_env=0-0-2&ns_ap_ut=60000&ns_ap_ar=unknown&ns_ap_cs=1&ns_ap_fg=1&ns_ap_dft=0&ns_ap_dbt=0&ns_ap_dit=0&ns_ap_as=1&ns_ap_das=0&ns_ap_usage=0&ns_radio=unknown&cs_ucfr=&ns_ap_install=1774999350619&ns_ap_ft=0&ns_ap_bt=0&ns_ap_it=0&ns_ap_res=3024x1724&ns_ap_sd=3600x2338&ns_ap_po=0x0&ns_ap_lang=en-US&ns_ap_jb=unknown&ns_c=UTF-8&c7=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&c8=MVPD%20Picker&c9=,sb.scorecardresearch.com,/p,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:54671,sb.scorecardresearch.com - 99.84.215.5:443,16:22:35.790,16:22:35.862,72,16:22:35.892,16:22:35.892,0,102,16:22:35.892,0,43,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2744,https://nbcu.track.securedvisit.com/citecapture/?cc_event=viewpage&cc_context=Custom%20View%20Page&sv_cid=5998_04670&sv_onetag_id=3977&sv_session=b57cedfd2f0e710b9203ebdc75fdb14e&sv_ver=2.0.2&sv_dt=2026-03-31T23%3A22%3A35.778Z&sv_referrer=&sv_url=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&sv_title=MVPD%20Picker&sv_keywords=&cc_data=%7B%22gK_KykkH%22%3A%22yWKie1WiK%22%2C%22gK_1D2_3l1GDgH_gzz01i_J%22%3A%22JTnn%22%7D,nbcu.track.securedvisit.com,/citecapture/,Completed,200,GET,application/javascript; charset=utf-8,Google Chrome,127.0.0.1:54641,nbcu.track.securedvisit.com - 52.9.185.121:443,16:22:35.781,16:22:35.781,0,16:22:35.836,16:22:35.836,0,55,16:22:35.836,0,0,0,0,,nbcu.track.securedvisit.com - 52.9.185.121:443,securedvisit.com,securedvisit.com; sv.rkdms.com; *.sv.rkdms.com; *.securedvisit.com; *.track.securedvisit.com; nbcu.track.securedvisit.com,
|
||||
2743,https://sp.auth.adobe.com/reggie/v1/nbcentertainment/regcode,sp.auth.adobe.com,/reggie/v1/nbcentertainment/regcode,Completed,201,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54666,sp.auth.adobe.com - 35.161.249.167:443,16:22:35.332,16:22:35.332,0,16:22:35.384,16:22:35.384,0,52,16:22:35.384,80,1977,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2742,https://sp.auth.adobe.com/reggie/v1/nbcentertainment/regcode,sp.auth.adobe.com,/reggie/v1/nbcentertainment/regcode,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54664,sp.auth.adobe.com - 35.161.249.167:443,16:22:35.288,16:22:35.288,0,16:22:35.331,16:22:35.331,0,43,16:22:35.331,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2741,https://sp.auth.adobe.com/indiv/devices,sp.auth.adobe.com,/indiv/devices,Completed,200,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54666,sp.auth.adobe.com - 35.161.249.167:443,16:22:35.074,16:22:35.178,104,16:22:35.285,16:22:35.285,0,211,16:22:35.285,50,47,0,67,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2740,https://sp.auth.adobe.com/indiv/devices,sp.auth.adobe.com,/indiv/devices,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54664,sp.auth.adobe.com - 35.161.249.167:443,16:22:34.916,16:22:35.022,106,16:22:35.065,16:22:35.065,0,149,16:22:35.065,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2739,https://nbcu.sv.rkdms.com/?sv_dt=2026-03-31T23%3A22%3A34.379Z&sv_cid=5998_04670&sv_uid=-8189650932773340651&sv_title=MVPD%20Picker&sv_referrer=&sv_url=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&sv_keywords=&sv_tzOffset=-0700&sv_inframe=false&sv_ver=2.0.2&sv_session=b57cedfd2f0e710b9203ebdc75fdb14e&sv_first=true&sv_px_domain_data=%22iHjobdQ1L1QHmw5yAfQvQWn3bhslbd01L1jIKqsID8t1mfjID89ebksJ2kt1LkbY2CnqmfjID89eKkhvQwz1KuYSgohD8ZtJ2xjlZJhT9hZjtPKQnWqyDrQvQWn3bhsGgC9GKdt1LkbY2Cnqet%22,nbcu.sv.rkdms.com,/,Completed,200,GET,image/gif,Google Chrome,127.0.0.1:54662,nbcu.sv.rkdms.com - 50.18.213.95:443,16:22:34.485,16:22:34.578,93,16:22:34.634,16:22:34.634,0,148,16:22:34.634,0,43,0,0,,nbcu.sv.rkdms.com - 50.18.213.95:443,securedvisit.com,securedvisit.com; sv.rkdms.com; *.sv.rkdms.com; *.securedvisit.com; *.track.securedvisit.com; nbcu.sv.rkdms.com,
|
||||
2737,https://entitlement.auth.adobe.com/entitlement/v4/4.js,entitlement.auth.adobe.com,/entitlement/v4/4.js,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54646,entitlement.auth.adobe.com - 23.42.82.205:443,16:22:34.383,16:22:34.463,80,16:22:34.482,16:22:34.483,0,100,16:22:34.483,0,608,0,359,,entitlement.auth.adobe.com - 23.42.82.205:443,ssl.adobe.com,ssl.adobe.com; signcsa.experienceleague.stage.adobe.com; wwwimages.stage2.adobe.com; www.macromedia.com; www.adobeidealab.com; cdn-stg-ffc.oobesaas.adobe.com; solutionpartners.stage2.adobe.com; www-stage01.acrobat.adobe.com; updates.adobeleanprint.com; download.stage.adobeprerelease.com; store2.stage2.adobe.com; live.adobeprimetime.com; wwwimages2.stage2.adobe.com; adobeprerelease.com; exchange.adobe.com; stage.status.adobe.com; experiencecloud.adobeexchange.com; www.stage.macromedia.com; experiencecloudstg2.adobeexchange.com; www.stage.adobeprerelease.com; www.qa.adobeprerelease.com; fpdownload.macromedia.com; stage.adobeprerelease.com; plan.adobe.com; dev.status.adobe.com; static.stage.adobeprimetime.com; preprod.status.adobe.com; live.stage.adobeprimetime.com; www.adobe.io; qe-ffc-static-cdn.oobesaas.adobe.com; download.adobeprerelease.com; trainingpartners.stage2.adobe.com; www.adobe-students.com; store1.stage2.adobe.com; auth.adobefpl.com; partners.stage2.adobe.com; media.stage.adobeprimetime.com; stage.adobe.io; cdn-qe-ffc.oobesaas.adobe.com; geo2.adobe.com; media.adobeprimetime.com; ffc-static-cdn.oobesaas.adobe.com; data.status.adobe.com; updates.stage.adobeleanprint.com; static.adobeprimetime.com; www.adobeprerelease.com; signcsa.experienceleague.adobe.com; hendrix360.qe.adobe.com; download.macromedia.com; csa.experienceleague.stage.adobe.com; app-cdn.stage2.adobe.com; stage.plan.adobe.com; www.stage2.adobe.com; stage.exchange.adobe.com; csa.experienceleague.adobe.com; entitlement.auth.adobe.com; download.qa.adobeprerelease.com; channelpartners.stage2.adobe.com; cdn-ffc.oobesaas.adobe.com; stg-ffc-static-cdn.oobesaas.adobe.com; technologypartners.stage2.adobe.com; www.stage.adobe-students.com,
|
||||
2736,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54650,map.mp.nbc.com - 151.101.2.49:443,16:22:34.383,16:22:34.446,63,16:22:34.482,16:22:34.482,0,100,16:22:34.482,958,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2735,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54651,map.mp.nbc.com - 151.101.2.49:443,16:22:34.383,16:22:34.452,69,16:22:34.482,16:22:34.482,0,100,16:22:34.482,173,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2734,https://entitlement.auth.adobe.com/entitlement/v4/3.js,entitlement.auth.adobe.com,/entitlement/v4/3.js,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54648,entitlement.auth.adobe.com - 23.42.82.205:443,16:22:34.383,16:22:34.461,78,16:22:34.479,16:22:34.479,0,97,16:22:34.479,0,34612,0,11660,,entitlement.auth.adobe.com - 23.42.82.205:443,ssl.adobe.com,ssl.adobe.com; signcsa.experienceleague.stage.adobe.com; wwwimages.stage2.adobe.com; www.macromedia.com; www.adobeidealab.com; cdn-stg-ffc.oobesaas.adobe.com; solutionpartners.stage2.adobe.com; www-stage01.acrobat.adobe.com; updates.adobeleanprint.com; download.stage.adobeprerelease.com; store2.stage2.adobe.com; live.adobeprimetime.com; wwwimages2.stage2.adobe.com; adobeprerelease.com; exchange.adobe.com; stage.status.adobe.com; experiencecloud.adobeexchange.com; www.stage.macromedia.com; experiencecloudstg2.adobeexchange.com; www.stage.adobeprerelease.com; www.qa.adobeprerelease.com; fpdownload.macromedia.com; stage.adobeprerelease.com; plan.adobe.com; dev.status.adobe.com; static.stage.adobeprimetime.com; preprod.status.adobe.com; live.stage.adobeprimetime.com; www.adobe.io; qe-ffc-static-cdn.oobesaas.adobe.com; download.adobeprerelease.com; trainingpartners.stage2.adobe.com; www.adobe-students.com; store1.stage2.adobe.com; auth.adobefpl.com; partners.stage2.adobe.com; media.stage.adobeprimetime.com; stage.adobe.io; cdn-qe-ffc.oobesaas.adobe.com; geo2.adobe.com; media.adobeprimetime.com; ffc-static-cdn.oobesaas.adobe.com; data.status.adobe.com; updates.stage.adobeleanprint.com; static.adobeprimetime.com; www.adobeprerelease.com; signcsa.experienceleague.adobe.com; hendrix360.qe.adobe.com; download.macromedia.com; csa.experienceleague.stage.adobe.com; app-cdn.stage2.adobe.com; stage.plan.adobe.com; www.stage2.adobe.com; stage.exchange.adobe.com; csa.experienceleague.adobe.com; entitlement.auth.adobe.com; download.qa.adobeprerelease.com; channelpartners.stage2.adobe.com; cdn-ffc.oobesaas.adobe.com; stg-ffc-static-cdn.oobesaas.adobe.com; technologypartners.stage2.adobe.com; www.stage.adobe-students.com,
|
||||
2733,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54647,map.mp.nbc.com - 151.101.2.49:443,16:22:34.383,16:22:34.447,65,16:22:34.485,16:22:34.485,0,102,16:22:34.485,880,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2732,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s18028242192904?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A34%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=nbcentertainment%3APC%3AMVPD%20Picker&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&network=NBC%20Entertainment&passnetwork=NBC&passmvpd=Verizon&passselected=true&title=nbcentertainment%3APC%3AMVPD%20Picker&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&.c&cc=USD&pe=lnk_o&pev2=Adobe%20Pass%3AVerizon%20Selected&c.&a.&activitymap.&page=nbcentertainment%3APC%3ANBC%20Sports%20Video%2C%20News%2C%20and%20Highlights%20%E2%80%93%20nbc.com&link=Verizon®ion=main&pageIDType=1&.activitymap&.a&.c&pid=nbcentertainment%3APC%3ANBC%20Sports%20Video%2C%20News%2C%20and%20Highlights%20%E2%80%93%20nbc.com&pidt=1&oid=functionjf%28%29%7B%7D&oidt=2&ot=BUTTON&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=157&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s18028242192904",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54649,nbcume.sc.omtrdc.net - 63.140.37.172:443,16:22:34.382,16:22:34.489,107,16:22:34.540,16:22:34.540,0,158,16:22:34.540,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.172:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2736,https://entitlement.auth.adobe.com/,entitlement.auth.adobe.com,/,Completed,200,GET,text/html,Google Chrome,127.0.0.1:54644,entitlement.auth.adobe.com - 23.42.82.205:443,16:22:34.381,16:22:34.447,66,16:22:34.469,16:22:34.469,0,88,16:22:34.469,0,26,0,46,,entitlement.auth.adobe.com - 23.42.82.205:443,ssl.adobe.com,ssl.adobe.com; signcsa.experienceleague.stage.adobe.com; wwwimages.stage2.adobe.com; www.macromedia.com; www.adobeidealab.com; cdn-stg-ffc.oobesaas.adobe.com; solutionpartners.stage2.adobe.com; www-stage01.acrobat.adobe.com; updates.adobeleanprint.com; download.stage.adobeprerelease.com; store2.stage2.adobe.com; live.adobeprimetime.com; wwwimages2.stage2.adobe.com; adobeprerelease.com; exchange.adobe.com; stage.status.adobe.com; experiencecloud.adobeexchange.com; www.stage.macromedia.com; experiencecloudstg2.adobeexchange.com; www.stage.adobeprerelease.com; www.qa.adobeprerelease.com; fpdownload.macromedia.com; stage.adobeprerelease.com; plan.adobe.com; dev.status.adobe.com; static.stage.adobeprimetime.com; preprod.status.adobe.com; live.stage.adobeprimetime.com; www.adobe.io; qe-ffc-static-cdn.oobesaas.adobe.com; download.adobeprerelease.com; trainingpartners.stage2.adobe.com; www.adobe-students.com; store1.stage2.adobe.com; auth.adobefpl.com; partners.stage2.adobe.com; media.stage.adobeprimetime.com; stage.adobe.io; cdn-qe-ffc.oobesaas.adobe.com; geo2.adobe.com; media.adobeprimetime.com; ffc-static-cdn.oobesaas.adobe.com; data.status.adobe.com; updates.stage.adobeleanprint.com; static.adobeprimetime.com; www.adobeprerelease.com; signcsa.experienceleague.adobe.com; hendrix360.qe.adobe.com; download.macromedia.com; csa.experienceleague.stage.adobe.com; app-cdn.stage2.adobe.com; stage.plan.adobe.com; www.stage2.adobe.com; stage.exchange.adobe.com; csa.experienceleague.adobe.com; entitlement.auth.adobe.com; download.qa.adobeprerelease.com; channelpartners.stage2.adobe.com; cdn-ffc.oobesaas.adobe.com; stg-ffc-static-cdn.oobesaas.adobe.com; technologypartners.stage2.adobe.com; www.stage.adobe-students.com,
|
||||
2730,https://nbcu.track.securedvisit.com/js/sv.js?sv_cid=5998_04670&sv_origin=nbc.com,nbcu.track.securedvisit.com,/js/sv.js,Completed,200,GET,application/javascript; charset=utf-8,Google Chrome,127.0.0.1:54641,nbcu.track.securedvisit.com - 52.9.185.121:443,16:22:34.203,16:22:34.320,118,16:22:34.372,16:22:34.375,3,172,16:22:34.375,0,65065,0,26619,,nbcu.track.securedvisit.com - 52.9.185.121:443,securedvisit.com,securedvisit.com; sv.rkdms.com; *.sv.rkdms.com; *.securedvisit.com; *.track.securedvisit.com; nbcu.track.securedvisit.com,
|
||||
2723,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999353200&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&c8=MVPD%20Picker&c9=,sb.scorecardresearch.com,/b,Completed,204,GET,,Google Chrome,127.0.0.1:54617,sb.scorecardresearch.com - 99.84.215.5:443,16:22:33.201,16:22:33.201,0,16:22:33.233,16:22:33.233,0,31,16:22:33.233,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2720,https://sb.scorecardresearch.com/b2?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999353054&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&c8=MVPD%20Picker&c9=,sb.scorecardresearch.com,/b2,Completed,204,GET,,Google Chrome,127.0.0.1:54617,sb.scorecardresearch.com - 99.84.215.5:443,16:22:33.086,16:22:33.086,0,16:22:33.187,16:22:33.187,0,101,16:22:33.187,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2719,https://api.nbc.com/localized-mvpd/entitlements?platform=iOS2&brand=nbcentertainment&instance=prod&zip=90404,api.nbc.com,/localized-mvpd/entitlements,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54616,api.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:33.067,16:22:33.191,124,16:22:33.255,16:22:33.265,10,198,16:22:33.265,0,554282,0,30727,,api.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; api.nbc.com,
|
||||
2718,https://sb.scorecardresearch.com/b?c1=2&c2=6035083&cs_it=b1&cv=4.13.1%2B2508250908&ns__t=1774999353054&ns_c=UTF-8&cs_cfg=1001110&cs_ucc=1&cs_cmp_id=28&cs_cmp_rt=0&cs_cmp_av=1.1&gpp_sid=7&gpp_smv=1.1&cs_cmp_ie=13&c7=https%3A%2F%2Fwww.nbc.com%2Fmvpd-picker&c8=MVPD%20Picker&c9=,sb.scorecardresearch.com,/b,Redirect,302,GET,,Google Chrome,127.0.0.1:54617,sb.scorecardresearch.com - 99.84.215.5:443,16:22:33.056,16:22:33.056,0,16:22:33.084,16:22:33.084,0,29,16:22:33.084,0,0,0,0,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2717,https://mps.nbcuni.com/request/page/json/params/?CALLBACK=mpsCallback&site=nbc-web&path=%2Fmvpd-picker&LOADMODE=more&ASYNC=1&_=1&NOLOAD=mpstools&USE_OVERLAY=0&IRSOURCE=false,mps.nbcuni.com,/request/page/json/params/,Completed,200,GET,application/json; charset=utf-8,Google Chrome,127.0.0.1:54618,mps.nbcuni.com - 23.192.167.152:443,16:22:32.955,16:22:33.024,69,16:22:33.187,16:22:33.189,2,234,16:22:33.189,0,37023,0,9968,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2716,https://sb.scorecardresearch.com/beacon.js,sb.scorecardresearch.com,/beacon.js,Completed,200,GET,text/javascript,Google Chrome,127.0.0.1:54617,sb.scorecardresearch.com - 99.84.215.5:443,16:22:32.955,16:22:33.033,78,16:22:33.051,16:22:33.051,0,96,16:22:33.051,0,21373,0,6468,,sb.scorecardresearch.com - 99.84.215.5:443,*.scorecardresearch.com,*.scorecardresearch.com; sb.scorecardresearch.com,
|
||||
2715,https://www.nbc.com/geoIp/insights,www.nbc.com,/geoIp/insights,Error,403,POST,text/html,Google Chrome,127.0.0.1:54611,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:32.947,16:22:32.948,0,16:22:33.004,16:22:33.004,0,57,16:22:33.004,47,383,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2714,https://www.nbc.com/generetic/generated/chunks/1637.9cbb36fc2327638c3691.js,www.nbc.com,/generetic/generated/chunks/1637.9cbb36fc2327638c3691.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54611,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:32.778,16:22:32.837,59,16:22:32.937,16:22:32.937,0,159,16:22:32.937,0,7896,0,2383,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2713,https://www.nbc.com/generetic/generated/chunks/4471.c44dd574d1063b58e85b.js,www.nbc.com,/generetic/generated/chunks/4471.c44dd574d1063b58e85b.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54610,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:32.778,16:22:32.842,64,16:22:32.923,16:22:32.923,0,145,16:22:32.923,0,53343,0,10452,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2714,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54555,map.mp.nbc.com - 151.101.2.49:443,16:22:32.775,16:22:32.775,0,16:22:32.813,16:22:32.813,0,38,16:22:32.813,973,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2713,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54553,map.mp.nbc.com - 151.101.2.49:443,16:22:32.775,16:22:32.775,0,16:22:32.808,16:22:32.808,0,33,16:22:32.808,173,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2711,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54556,map.mp.nbc.com - 151.101.2.49:443,16:22:32.774,16:22:32.775,0,16:22:32.803,16:22:32.803,0,29,16:22:32.803,886,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2707,https://www.nbc.com/generetic/favicon.ico,www.nbc.com,/generetic/favicon.ico,Completed,200,GET,image/x-icon,Google Chrome,127.0.0.1:54599,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:32.736,16:22:32.798,62,16:22:32.848,16:22:32.848,0,112,16:22:32.848,0,9662,0,1561,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2702,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54556,map.mp.nbc.com - 151.101.2.49:443,16:22:31.986,16:22:31.987,0,16:22:32.016,16:22:32.016,0,29,16:22:32.016,872,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2697,https://img.nbc.com/files/2024-11/nbcsportsnow_white_logo-425x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-11/nbcsportsnow_white_logo-425x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54585,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:31.337,16:22:31.395,58,16:22:31.439,16:22:31.439,0,102,16:22:31.439,0,7600,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2698,https://img.nbc.com/files/2024-01/nbc-news-now_white_stacked.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-01/nbc-news-now_white_stacked.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54584,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:31.337,16:22:31.404,67,16:22:31.456,16:22:31.456,0,119,16:22:31.456,0,15256,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2696,https://img.nbc.com/files/images/2019/3/11/Bravo-Logo-All-platform-AssetsBravo-Logo-White-905x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/images/2019/3/11/Bravo-Logo-All-platform-AssetsBravo-Logo-White-905x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54583,img.nbc.com - 2600:1406:4e00:19::1738:6d50:443,16:22:31.337,16:22:31.401,64,16:22:31.469,16:22:31.469,0,131,16:22:31.469,0,4766,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d50:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2695,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Afalse%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22sport%22%2C%22type%22%3A%22LANDING_PAGE%22%2C%22subType%22%3A%22sportHome%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22appVersion%22%3A1249000%2C%22componentConfigs%22%3A%5B%22eyJ0aXRsZSI6IkZhdm9yaXRlcyIsInR5cGUiOiJTaGVsZiIsImltcGxlbWVudGF0aW9uIjoiZmF2b3JpdGVzU2hlbGYiLCJuYW1lIjoiZmF2b3JpdGVzU2hlbGYiLCJhcHAiOiJuYmMifQ%3D%3D%22%2C%22eyJ0eXBlIjoiU2hlbGYiLCJpbXBsZW1lbnRhdGlvbiI6ImF1dG9tYXRlZExpbmVhclNoZWxmIiwibmFtZSI6ImF1dG9tYXRlZExpbmVhclNoZWxmIiwiYXBwIjoibmJjIn0%3D%22%5D%2C%22queryName%22%3A%22componentsForPlaceholders%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%223b7ab0262b412e1a69aa09333228ebcf55cf4f40a56cff0769607c6f0743a36a%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54567,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:31.253,16:22:31.253,0,16:22:31.323,16:22:31.323,0,70,16:22:31.323,0,10255,0,2421,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2691,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54556,map.mp.nbc.com - 151.101.2.49:443,16:22:30.799,16:22:30.799,0,16:22:30.832,16:22:30.832,0,33,16:22:30.832,17094,42,0,62,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2688,https://map.mp.nbc.com/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,map.mp.nbc.com,/webevents/v3/JS/69dedba1e9714049b35bde9e2f9bf059/events,Completed,202,POST,application/json,Google Chrome,127.0.0.1:54556,map.mp.nbc.com - 151.101.2.49:443,16:22:30.734,16:22:30.734,0,16:22:30.798,16:22:30.798,0,64,16:22:30.798,10567,40,0,60,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2687,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54553,map.mp.nbc.com - 151.101.2.49:443,16:22:30.734,16:22:30.734,0,16:22:30.765,16:22:30.765,0,31,16:22:30.765,1227,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2686,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54555,map.mp.nbc.com - 151.101.2.49:443,16:22:30.729,16:22:30.729,0,16:22:30.763,16:22:30.763,0,34,16:22:30.763,1221,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2684,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54552,map.mp.nbc.com - 151.101.2.49:443,16:22:30.716,16:22:30.716,0,16:22:30.750,16:22:30.750,0,34,16:22:30.750,914,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2683,https://friendship.nbc.com/v3/graphql?variables=%7B%22appVersion%22%3A1249000%2C%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Afalse%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22sport%22%2C%22type%22%3A%22LANDING_PAGE%22%2C%22subType%22%3A%22sportHome%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22queryName%22%3A%22featuredSection%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22a76f2966d3fbe90881952db8e3b69f211d84c9c3d2d1d7cd9368378ece8e930e%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54568,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.703,16:22:30.776,73,16:22:30.914,16:22:30.914,0,211,16:22:30.914,0,23822,0,3761,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2682,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22device%22%3A%22web%22%2C%22platform%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22authorized%22%3Afalse%2C%22isDayZero%22%3Atrue%2C%22name%22%3A%22sport%22%2C%22type%22%3A%22LANDING_PAGE%22%2C%22subType%22%3A%22sportHome%22%2C%22timeZone%22%3A%22America%2FLos_Angeles%22%2C%22nbcAffiliateName%22%3A%22knbc%22%2C%22telemundoAffiliateName%22%3A%22kvea%22%2C%22nationalBroadcastType%22%3A%22westCoast%22%2C%22app%22%3A%22nbc%22%2C%22appVersion%22%3A1249000%2C%22queryName%22%3A%22page%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22661bcb56e11fbe0692168bc41f864967ba8bbaa160985099c3092877e9b8dddc%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54567,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.703,16:22:30.772,69,16:22:31.229,16:22:31.229,1,527,16:22:31.229,0,381036,0,45214,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2681,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54559,map.mp.nbc.com - 151.101.2.49:443,16:22:30.614,16:22:30.706,92,16:22:30.737,16:22:30.737,0,123,16:22:30.737,917,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2681,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54557,map.mp.nbc.com - 151.101.2.49:443,16:22:30.613,16:22:30.704,92,16:22:30.735,16:22:30.735,0,122,16:22:30.735,6670,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2680,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54555,map.mp.nbc.com - 151.101.2.49:443,16:22:30.613,16:22:30.692,79,16:22:30.728,16:22:30.728,0,116,16:22:30.728,6716,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2679,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54556,map.mp.nbc.com - 151.101.2.49:443,16:22:30.612,16:22:30.698,85,16:22:30.734,16:22:30.734,0,121,16:22:30.734,1278,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2678,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54553,map.mp.nbc.com - 151.101.2.49:443,16:22:30.612,16:22:30.698,85,16:22:30.734,16:22:30.734,0,121,16:22:30.734,1046,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2680,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54552,map.mp.nbc.com - 151.101.2.49:443,16:22:30.608,16:22:30.685,77,16:22:30.716,16:22:30.716,0,108,16:22:30.716,1276,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2672,https://www.nbc.com/firebase-messaging-sw.js,www.nbc.com,/firebase-messaging-sw.js,Notmodified,304,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54544,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.479,16:22:30.479,0,16:22:30.568,16:22:30.568,0,89,16:22:30.568,0,0,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2671,https://www.nbc.com/generetic/scripts/comScore-7.6.0.min.js,www.nbc.com,/generetic/scripts/comScore-7.6.0.min.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54532,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.466,16:22:30.466,0,16:22:30.568,16:22:30.569,1,103,16:22:30.569,0,170726,0,47385,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2670,https://www.nbc.com/firebase-messaging-sw.js,www.nbc.com,/firebase-messaging-sw.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54544,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.363,16:22:30.427,65,16:22:30.472,16:22:30.474,2,112,16:22:30.474,0,163333,0,33994,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2669,https://img.nbc.com/files/images/2021/6/29/elements-search-icon-active.png,img.nbc.com,/files/images/2021/6/29/elements-search-icon-active.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54535,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:30.353,16:22:30.429,76,16:22:30.472,16:22:30.472,0,120,16:22:30.472,0,764,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2668,https://img.nbc.com/files/images/2021/6/30/elements-search-rollover-hover.png,img.nbc.com,/files/images/2021/6/30/elements-search-rollover-hover.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54536,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:30.352,16:22:30.430,78,16:22:30.470,16:22:30.470,0,117,16:22:30.470,0,694,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2667,https://img.nbc.com/files/images/2021/6/29/elements-search-icon.png,img.nbc.com,/files/images/2021/6/29/elements-search-icon.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54533,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:30.352,16:22:30.426,74,16:22:30.461,16:22:30.461,0,109,16:22:30.461,0,746,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2666,https://img.nbc.com/files/images/2021/6/29/peacock_preview.png,img.nbc.com,/files/images/2021/6/29/peacock_preview.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54534,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:30.352,16:22:30.420,67,16:22:30.461,16:22:30.461,0,109,16:22:30.461,0,5043,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2665,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22-8189650932773340651%22%2C%22platform%22%3A%22web%22%2C%22app%22%3A%22nbc%22%2C%22queryName%22%3A%22userInteractions%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%222fc3608e4ca15d6964cf319391ce4d24ba29f37e484e0a3afe63e788b0c7e594%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54537,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.352,16:22:30.431,79,16:22:30.568,16:22:30.568,0,216,16:22:30.568,0,67,0,0,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2664,https://www.nbc.com/generetic/generated/images/4fac8d8aaa7d8798a512dac0322d8d14.png,www.nbc.com,/generetic/generated/images/4fac8d8aaa7d8798a512dac0322d8d14.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54532,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:30.346,16:22:30.408,63,16:22:30.454,16:22:30.454,0,108,16:22:30.454,0,7219,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2656,https://sp.auth.adobe.com/adobe-services/config/nbcentertainment,sp.auth.adobe.com,/adobe-services/config/nbcentertainment,Completed,200,GET,application/xml;charset=UTF-8,Google Chrome,127.0.0.1:54494,sp.auth.adobe.com - 35.161.249.167:443,16:22:30.151,16:22:30.151,0,16:22:30.208,16:22:30.208,0,57,16:22:30.208,0,192247,0,12463,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2655,https://sp.auth.adobe.com/adobe-services/config/nbcentertainment,sp.auth.adobe.com,/adobe-services/config/nbcentertainment,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54461,sp.auth.adobe.com - 35.161.249.167:443,16:22:30.104,16:22:30.104,0,16:22:30.150,16:22:30.150,0,46,16:22:30.150,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2654,https://mps.nbcuni.com/images/MPS-STATISTIC-REPORTING.png?X=hasab&AB=0&S=nbc-web&H=www.nbc.com&P=%2Fsports&D=desktop&U=Mozilla%2F5.0%20(Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F146.0.0.0%20Safari%2F537.36&_=daeqwtokpvfx,mps.nbcuni.com,/images/MPS-STATISTIC-REPORTING.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54517,mps.nbcuni.com - 23.192.167.152:443,16:22:30.088,16:22:30.138,50,16:22:30.251,16:22:30.251,0,163,16:22:30.251,0,84,0,0,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2652,https://sp.auth.adobe.com/o/client/token,sp.auth.adobe.com,/o/client/token,Completed,201,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54494,sp.auth.adobe.com - 35.161.249.167:443,16:22:30.042,16:22:30.042,0,16:22:30.102,16:22:30.102,0,60,16:22:30.102,127,767,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2651,https://sp.auth.adobe.com/o/client/token,sp.auth.adobe.com,/o/client/token,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54461,sp.auth.adobe.com - 35.161.249.167:443,16:22:29.959,16:22:29.959,0,16:22:30.041,16:22:30.041,0,82,16:22:30.041,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2642,https://map.mp.nbc.com/identity/v1/identify,map.mp.nbc.com,/identity/v1/identify,Completed,200,POST,application/json; charset=utf-8,Google Chrome,127.0.0.1:54468,map.mp.nbc.com - 151.101.2.49:443,16:22:29.799,16:22:29.799,0,16:22:29.887,16:22:29.887,0,87,16:22:29.887,384,161,0,153,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2641,https://sp.auth.adobe.com/o/client/register,sp.auth.adobe.com,/o/client/register,Completed,201,POST,application/json;charset=UTF-8,Google Chrome,127.0.0.1:54494,sp.auth.adobe.com - 35.161.249.167:443,16:22:29.792,16:22:29.909,117,16:22:29.958,16:22:29.958,0,166,16:22:29.958,527,310,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2641,https://ss.nbc.co/conveyor/search?mpid=874355218828044267,ss.nbc.co,/conveyor/search,Error,404,GET,application/json,Google Chrome,127.0.0.1:54493,ss.nbc.co - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.790,16:22:29.891,102,16:22:30.195,16:22:30.195,0,406,16:22:30.195,0,132,0,0,,ss.nbc.co - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.co,*.nbc.co; nbc.co; ss.nbc.co,
|
||||
2635,https://friendship.nbc.com/v3/graphql?variables=%7B%22userId%22%3A%22874355218828044267%22%2C%22platform%22%3A%22web%22%2C%22device%22%3A%22web%22%2C%22language%22%3A%22en%22%2C%22app%22%3A%22nbc%22%2C%22isDayZero%22%3Atrue%2C%22queryName%22%3A%22globalNavigation%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%2233e6c80b28860c5c634c6c4f10b2cb7de6e5dd54fbcec05138055ad9125cee63%22%7D%7D,friendship.nbc.com,/v3/graphql,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54483,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.743,16:22:29.836,93,16:22:29.931,16:22:29.931,0,188,16:22:29.931,0,2223,0,467,,friendship.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; friendship.nbc.com,
|
||||
2634,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s11966585529175?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A29%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=nbcentertainment%3APC%3ANBC%20Sports%20Video%2C%20News%2C%20and%20Highlights%20%E2%80%93%20nbc.com&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&network=NBC%20Entertainment&identityguid=874355218828044267&identityauthen=Unauthenticated&identitymethod=None&title=nbcentertainment%3APC%3ANBC%20Sports%20Video%2C%20News%2C%20and%20Highlights%20%E2%80%93%20nbc.com&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&.c&cc=USD&pe=lnk_o&pev2=Identity%20Sign-In%20Check&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&lrt=191&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s11966585529175",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54482,nbcume.sc.omtrdc.net - 63.140.37.172:443,16:22:29.742,16:22:29.844,102,16:22:29.894,16:22:29.894,0,152,16:22:29.894,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.172:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2632,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54470,map.mp.nbc.com - 151.101.2.49:443,16:22:29.701,16:22:29.767,67,16:22:29.808,16:22:29.808,0,107,16:22:29.808,155,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2631,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54468,map.mp.nbc.com - 151.101.2.49:443,16:22:29.701,16:22:29.765,65,16:22:29.799,16:22:29.799,0,98,16:22:29.799,155,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2630,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54467,map.mp.nbc.com - 151.101.2.49:443,16:22:29.700,16:22:29.765,65,16:22:29.794,16:22:29.794,0,94,16:22:29.794,155,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2629,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54469,map.mp.nbc.com - 151.101.2.49:443,16:22:29.700,16:22:29.763,63,16:22:29.791,16:22:29.791,0,90,16:22:29.791,155,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2625,https://map.mp.nbc.com/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,map.mp.nbc.com,/webevents/v1/JS/69dedba1e9714049b35bde9e2f9bf059/Forwarding,Completed,202,POST,,Google Chrome,127.0.0.1:54441,map.mp.nbc.com - 151.101.2.49:443,16:22:29.695,16:22:29.695,0,16:22:29.724,16:22:29.724,0,29,16:22:29.724,155,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2622,https://sp.auth.adobe.com/o/client/register,sp.auth.adobe.com,/o/client/register,Completed,200,OPTIONS,,Google Chrome,127.0.0.1:54461,sp.auth.adobe.com - 35.161.249.167:443,16:22:29.623,16:22:29.747,124,16:22:29.788,16:22:29.788,0,166,16:22:29.788,0,0,0,0,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2621,https://img.nbc.com/files/12012833_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12012833_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.604,16:22:29.604,0,16:22:29.653,16:22:29.656,2,52,16:22:29.656,0,22144,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2619,https://map.mp.nbc.com/identity/v1/identify,map.mp.nbc.com,/identity/v1/identify,Completed,200,POST,application/json; charset=utf-8,Google Chrome,127.0.0.1:54441,map.mp.nbc.com - 151.101.2.49:443,16:22:29.589,16:22:29.589,0,16:22:29.692,16:22:29.692,0,103,16:22:29.692,318,175,0,163,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2618,https://img.nbc.com/files/12014237_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014237_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.568,16:22:29.568,0,16:22:29.617,16:22:29.617,0,49,16:22:29.617,0,15197,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2617,https://sp.auth.adobe.com/entitlement/v4/AccessEnablerProxy.js,sp.auth.adobe.com,/entitlement/v4/AccessEnablerProxy.js,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54429,sp.auth.adobe.com - 35.161.249.167:443,16:22:29.559,16:22:29.559,0,16:22:29.612,16:22:29.612,0,53,16:22:29.612,0,6012,0,2450,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2615,https://img.nbc.com/files/12014273_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014273_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.557,16:22:29.557,0,16:22:29.603,16:22:29.603,0,47,16:22:29.603,0,15146,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2614,https://img.nbc.com/files/12014303_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014303_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.520,16:22:29.520,0,16:22:29.567,16:22:29.568,1,47,16:22:29.568,0,33588,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2613,https://img.nbc.com/files/12014222_1920x1080.jpg?impolicy=nbc_com&imwidth=480&imdensity=1,img.nbc.com,/files/12014222_1920x1080.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.445,16:22:29.445,0,16:22:29.555,16:22:29.555,0,110,16:22:29.555,0,16992,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2611,https://mps.nbcuni.com/request/page/json/params/?CALLBACK=mpsCallback&cat=sportspage&site=nbc-web&type=sportspage&path=%2F&title=NBC%20Sports%20Video%20News%20and%20Highlights%20%20nbc.com&cag%5Bsubdomains%5D=www&cag%5Btopdomain%5D=nbc.com&field%5Bpageurl%5D=www.nbc.com%2Fsports&NOLOAD=mpstools&USE_OVERLAY=0&IRSOURCE=false&ASYNC=1,mps.nbcuni.com,/request/page/json/params/,Completed,200,GET,application/json; charset=utf-8,Google Chrome,127.0.0.1:54448,mps.nbcuni.com - 23.192.167.152:443,16:22:29.441,16:22:29.558,117,16:22:29.653,16:22:29.654,0,212,16:22:29.654,0,40289,0,9930,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2609,"https://nbcume.sc.omtrdc.net/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15552565633701?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A29%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=ISO-8859-1&pageName=global%3Ahome&g=https%3A%2F%2Fwww.nbc.com%2Fsports&c.&tve.&contenthub=Adobe%20Pass&network=NBC%20Entertainment&title=global%3Ahome&domain=www.nbc.com&platform=PC&did=demdex%20cookie%20not%20set&date=03%2F31%2F2026&day=Tuesday&hour=16%3A00&minute=16%3A22&.tve&nbcu.&contentGroup=Online&contentType=Home&showSite=Global&.nbcu&pageTitle=Home&.c&cc=USD&server=www.nbc.com&aamb=RKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&AQE=1",nbcume.sc.omtrdc.net,"/b/ss/nbcutve,nbcunetworkbu/1/JS-2.24.0-LEWM/s15552565633701",Completed,200,GET,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54442,nbcume.sc.omtrdc.net - 63.140.37.172:443,16:22:29.432,16:22:29.575,144,16:22:29.618,16:22:29.618,0,187,16:22:29.618,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.172:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
2608,https://map.mp.nbc.com/identity/v1/identify,map.mp.nbc.com,/identity/v1/identify,Completed,204,OPTIONS,,Google Chrome,127.0.0.1:54441,map.mp.nbc.com - 151.101.2.49:443,16:22:29.428,16:22:29.567,138,16:22:29.588,16:22:29.588,0,160,16:22:29.588,0,0,0,0,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2608,https://img.nbc.com/files/images/2019/4/16/Telemundo-Logo-White-360x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/images/2019/4/16/Telemundo-Logo-White-360x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.422,16:22:29.422,0,16:22:29.519,16:22:29.520,0,97,16:22:29.520,0,4704,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2602,https://img.nbc.com/files/2024-08/nbcsports-boston-logo-white-375x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-08/nbcsports-boston-logo-white-375x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.395,16:22:29.395,0,16:22:29.444,16:22:29.444,0,49,16:22:29.444,0,15022,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2601,https://img.nbc.com/files/2024-08/nbcsports-bay-area-logo-white-382x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-08/nbcsports-bay-area-logo-white-382x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.380,16:22:29.380,0,16:22:29.421,16:22:29.421,0,41,16:22:29.421,0,15232,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2600,https://img.nbc.com/files/2024-08/nbcsports-california-logo-white-378x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-08/nbcsports-california-logo-white-378x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.345,16:22:29.345,0,16:22:29.379,16:22:29.379,0,34,16:22:29.379,0,15814,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2599,https://img.nbc.com/files/images/2019/4/25/Universo-logos-templateUniverso-Logo-White-450x228.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/images/2019/4/25/Universo-logos-templateUniverso-Logo-White-450x228.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.315,16:22:29.315,0,16:22:29.395,16:22:29.395,0,80,16:22:29.395,0,7468,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2598,https://mps.nbcuni.com/fetch/ext/load-nbc-web.js?nowrite=2,mps.nbcuni.com,/fetch/ext/load-nbc-web.js,Completed,200,GET,application/javascript; charset=utf-8,Google Chrome,127.0.0.1:54430,mps.nbcuni.com - 23.192.167.152:443,16:22:29.312,16:22:29.387,75,16:22:29.412,16:22:29.436,24,124,16:22:29.436,0,279843,0,64317,,mps.nbcuni.com - 23.192.167.152:443,*.nbcuni.com,*.nbcuni.com; nbcuni.com; mps.nbcuni.com,
|
||||
2596,https://sp.auth.adobe.com/entitlement/v4/AccessEnablerProxy.html?9f61ce3349740cda7528,sp.auth.adobe.com,/entitlement/v4/AccessEnablerProxy.html,Completed,200,GET,text/html,Google Chrome,127.0.0.1:54429,sp.auth.adobe.com - 35.161.249.167:443,16:22:29.283,16:22:29.445,161,16:22:29.555,16:22:29.555,0,272,16:22:29.555,0,235,0,181,,sp.auth.adobe.com - 35.161.249.167:443,*.auth.adobe.com,*.auth.adobe.com; api-gateway-ap-prd-va6-02-b.infra.adobe.io; adobepass-release-production-cg.ethos551-prod-va6.ethos.adobe.net; sp-us-west-2-az1.adobepass.com; sp-gw-us-west-2.adobepass.com; sp-us-east-1.adobepass.com; api-gateway-ap-prd-va6-01.infra.adobe.io; api-gateway-ap-prd-va6-01-a.infra.adobe.io; sp-gw-prod2-us-east-1.adobepass.com; sp-us-east-1-az2.adobepass.com; adobepass-release-production-cg.ethos501-prod-or2.ethos.adobe.net; api-gateway-ap-prd-or2-a.infra.adobe.io; adobepass-release-production-cg.ethos501-prod-va6.ethos.adobe.net; sp-dc.adobepass.com; sp-gw-us-east-1.adobepass.com; api-gateway-ap-prd-or2.infra.adobe.io; api-gateway-ap-prd-or2-b.infra.adobe.io; api-gateway-ap-prd-va6-02.infra.adobe.io; api-gateway-ap-prd-va6-02-a.infra.adobe.io; api-gateway-ap-prd-va6-01-b.infra.adobe.io; sp.auth.adobe.com,
|
||||
2595,https://map.mp.nbc.com/tags/JS/v2/69dedba1e9714049b35bde9e2f9bf059/config?env=0&plan_id=nbcu_data_plan,map.mp.nbc.com,/tags/JS/v2/69dedba1e9714049b35bde9e2f9bf059/config,Completed,200,GET,text/plain; charset=utf-8,Google Chrome,127.0.0.1:54428,map.mp.nbc.com - 151.101.2.49:443,16:22:29.280,16:22:29.375,95,16:22:29.390,16:22:29.419,29,139,16:22:29.419,0,472348,0,190151,,map.mp.nbc.com - 151.101.2.49:443,map.mp.nbc.com,map.mp.nbc.com,
|
||||
2593,https://img.nbc.com/files/2024-08/nbcsports-philadelphia-logo-white-378x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-08/nbcsports-philadelphia-logo-white-378x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.253,16:22:29.253,0,16:22:29.314,16:22:29.314,0,61,16:22:29.314,0,16244,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2592,https://img.nbc.com/files/2025-11/smallball_s1-logo-white-602x428.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2025-11/smallball_s1-logo-white-602x428.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.187,16:22:29.187,0,16:22:29.250,16:22:29.252,2,66,16:22:29.252,0,17790,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2591,"https://img.nbc.com/files/2026-03/1920x1080-westdreamsb-2026-web-dynamiclead-desktop.jpg?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/1920x1080-westdreamsb-2026-web-dynamiclead-desktop.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.168,16:22:29.168,0,16:22:29.338,16:22:29.345,7,177,16:22:29.345,0,113461,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2588,https://img.nbc.com/files/2026-03/menscollegebasketball-logo-color-311x428.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2026-03/menscollegebasketball-logo-color-311x428.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.141,16:22:29.141,0,16:22:29.186,16:22:29.186,0,46,16:22:29.186,0,15214,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2587,https://www.nbc.com/generetic/generated/chunks/7380.b12c50e3d1ef34d42b66.js,www.nbc.com,/generetic/generated/chunks/7380.b12c50e3d1ef34d42b66.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54378,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.078,16:22:29.078,0,16:22:29.271,16:22:29.271,0,193,16:22:29.271,0,12002,0,2832,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2586,https://www.nbc.com/generetic/generated/chunks/1944.f06c416df55bf341a86b.js,www.nbc.com,/generetic/generated/chunks/1944.f06c416df55bf341a86b.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54376,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.078,16:22:29.078,0,16:22:29.138,16:22:29.141,3,63,16:22:29.141,0,162955,0,27016,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2585,https://www.nbc.com/generetic/generated/chunks/8817.10b93d60c41cad6f391d.js,www.nbc.com,/generetic/generated/chunks/8817.10b93d60c41cad6f391d.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54377,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.078,16:22:29.078,0,16:22:29.126,16:22:29.126,0,48,16:22:29.126,0,44216,0,6322,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2584,https://www.nbc.com/generetic/generated/chunks/4577.5cff97b09ba86735c8b6.js,www.nbc.com,/generetic/generated/chunks/4577.5cff97b09ba86735c8b6.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54373,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:29.078,16:22:29.078,0,16:22:29.232,16:22:29.232,0,154,16:22:29.232,0,92084,0,15867,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2583,"https://img.nbc.com/files/2026-03/michigan_033026_1920.jpg?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/michigan_033026_1920.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.073,16:22:29.073,0,16:22:29.160,16:22:29.167,7,94,16:22:29.167,0,91758,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2582,"https://img.nbc.com/files/2026-03/warren_033026_1920.jpg?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/warren_033026_1920.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.029,16:22:29.029,0,16:22:29.131,16:22:29.140,9,112,16:22:29.140,0,78758,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2581,https://img.nbc.com/files/2025-10/nbaonnbc-logo-color-508x428.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2025-10/nbaonnbc-logo-color-508x428.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:29.018,16:22:29.018,0,16:22:29.071,16:22:29.073,2,55,16:22:29.073,0,25080,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2579,"https://img.nbc.com/files/2026-03/1920x1080-detokchl0330-2026-web-dynamiclead-desktop.jpg?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/1920x1080-detokchl0330-2026-web-dynamiclead-desktop.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.871,16:22:28.871,0,16:22:28.996,16:22:29.017,21,146,16:22:29.017,0,133203,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2578,https://img.nbc.com/files/2025-10/ncaa_mens_college_basketball.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-10/ncaa_mens_college_basketball.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.847,16:22:28.847,0,16:22:29.028,16:22:29.028,0,181,16:22:29.028,0,2236,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2577,https://img.nbc.com/files/2025-05/nba_league.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/nba_league.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.839,16:22:28.839,0,16:22:28.870,16:22:28.870,0,31,16:22:28.870,0,1720,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2576,https://img.nbc.com/files/2025-05/premier_league.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/premier_league.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.798,16:22:28.798,0,16:22:28.838,16:22:28.838,0,40,16:22:28.838,0,1520,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2575,https://assets.adobedtm.com/a2ef59fba8e9/9c7a708dbcb2/d3d66595eddb/RCde3a0129d7334bb0bf2895038d0c6952-source.min.js,assets.adobedtm.com,/a2ef59fba8e9/9c7a708dbcb2/d3d66595eddb/RCde3a0129d7334bb0bf2895038d0c6952-source.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54415,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:28.794,16:22:28.865,72,16:22:28.879,16:22:28.880,0,86,16:22:28.880,0,532,0,314,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2574,https://assets.adobedtm.com/extensions/EPe51f9b26f7c243dfa8d1d3ea2bf16f5f/AppMeasurement_Module_ActivityMap.min.js,assets.adobedtm.com,/extensions/EPe51f9b26f7c243dfa8d1d3ea2bf16f5f/AppMeasurement_Module_ActivityMap.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54414,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:28.794,16:22:28.859,65,16:22:28.876,16:22:28.876,0,83,16:22:28.876,0,3284,0,1597,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2573,https://img.nbc.com/files/2025-05/nhl.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/nhl.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.785,16:22:28.785,0,16:22:28.846,16:22:28.846,0,61,16:22:28.846,0,2842,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2571,https://assets.adobedtm.com/extensions/EPe51f9b26f7c243dfa8d1d3ea2bf16f5f/AppMeasurement.min.js,assets.adobedtm.com,/extensions/EPe51f9b26f7c243dfa8d1d3ea2bf16f5f/AppMeasurement.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54399,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:28.784,16:22:28.784,0,16:22:28.799,16:22:28.799,0,15,16:22:28.799,0,34560,0,12463,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2569,https://img.nbc.com/files/2026-03/snbb_reverse_rgb.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2026-03/snbb_reverse_rgb.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.755,16:22:28.755,0,16:22:28.794,16:22:28.798,4,43,16:22:28.798,0,27656,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2568,https://img.nbc.com/files/2025-05/nascar.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/nascar.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.721,16:22:28.721,0,16:22:28.755,16:22:28.755,0,33,16:22:28.755,0,2007,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2567,https://img.nbc.com/files/2025-05/golf-icon.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/golf-icon.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.704,16:22:28.704,0,16:22:28.785,16:22:28.785,0,81,16:22:28.785,0,972,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2566,https://img.nbc.com/files/2025-08/ncaa_mens_college_football.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-08/ncaa_mens_college_football.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.656,16:22:28.656,0,16:22:28.704,16:22:28.704,0,48,16:22:28.704,0,2302,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2565,"https://img.nbc.com/files/2026-03/emerson-2023-web-dynamiclead-desktop-1920x1080.png?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/emerson-2023-web-dynamiclead-desktop-1920x1080.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.654,16:22:28.654,0,16:22:28.694,16:22:28.721,27,67,16:22:28.721,0,59692,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2564,https://img.nbc.com/files/2025-06/all_sports_text_53x20_4x.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-06/all_sports_text_53x20_4x.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.614,16:22:28.614,0,16:22:28.655,16:22:28.655,0,42,16:22:28.655,0,928,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2563,https://img.nbc.com/files/2025-05/big_ten_conference.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/big_ten_conference.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.613,16:22:28.613,0,16:22:28.653,16:22:28.653,0,40,16:22:28.653,0,1316,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2562,https://img.nbc.com/files/2025-05/mlb_league.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/mlb_league.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.582,16:22:28.582,0,16:22:28.613,16:22:28.613,0,31,16:22:28.613,0,1362,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2561,https://img.nbc.com/files/2025-05/nfl_league.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/nfl_league.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.576,16:22:28.576,0,16:22:28.612,16:22:28.612,0,37,16:22:28.612,0,2244,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2560,https://www.nbc.com/generetic/generated/images/88e243732529f35b45f990fcb2125014.png,www.nbc.com,/generetic/generated/images/88e243732529f35b45f990fcb2125014.png,Completed,200,GET,image/png,Google Chrome,127.0.0.1:54378,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.540,16:22:28.540,0,16:22:28.577,16:22:28.577,0,38,16:22:28.577,0,5439,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2559,https://www.nbc.com/generetic/scripts/ads.js,www.nbc.com,/generetic/scripts/ads.js,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54375,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.535,16:22:28.535,0,16:22:28.573,16:22:28.573,0,38,16:22:28.573,0,22,0,42,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2558,https://img.nbc.com/files/2025-05/wnba-league.png?impolicy=nbc_com&imwidth=45&imdensity=1,img.nbc.com,/files/2025-05/wnba-league.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.525,16:22:28.525,0,16:22:28.575,16:22:28.575,0,50,16:22:28.575,0,1554,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2557,https://www.nbc.com/generetic/generated/fonts/MontserratBold.woff2,www.nbc.com,/generetic/generated/fonts/MontserratBold.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.521,16:22:28.521,0,16:22:28.571,16:22:28.571,0,50,16:22:28.571,0,16064,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2556,https://www.nbc.com/generetic/generated/fonts/MontserratRegular.woff2,www.nbc.com,/generetic/generated/fonts/MontserratRegular.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54375,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.473,16:22:28.473,0,16:22:28.534,16:22:28.535,0,62,16:22:28.535,0,16136,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2555,https://www.nbc.com/generetic/generated/fonts/AcuminProSemiCondensed_700.woff2,www.nbc.com,/generetic/generated/fonts/AcuminProSemiCondensed_700.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.472,16:22:28.473,0,16:22:28.520,16:22:28.520,0,48,16:22:28.520,0,47016,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2554,https://www.nbc.com/generetic/generated/fonts/nbciconfont.woff2,www.nbc.com,/generetic/generated/fonts/nbciconfont.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.372,16:22:28.372,0,16:22:28.470,16:22:28.471,0,98,16:22:28.471,0,15956,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2553,https://assets.adobedtm.com/a2ef59fba8e9/9c7a708dbcb2/launch-678397df18e0.min.js,assets.adobedtm.com,/a2ef59fba8e9/9c7a708dbcb2/launch-678397df18e0.min.js,Completed,200,GET,application/x-javascript,Google Chrome,127.0.0.1:54399,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,16:22:28.282,16:22:28.718,436,16:22:28.735,16:22:28.777,43,495,16:22:28.777,0,330196,0,80730,,assets.adobedtm.com - 2600:1406:5400:3a6::1e80:443,assets.adobedtm.com,assets.adobedtm.com; cdn1-staging.adoberesources.net; assets.adoberesources.net; assets-staging.adoberesources.net; cdn1.adoberesources.net; commerce.adobedtm.com; magento-recs-sdk.adobe.net,
|
||||
2552,https://img.nbc.com/files/2025-09/pft-logo-color-900x302.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2025-09/pft-logo-color-900x302.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54398,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.282,16:22:32.672,4390,16:22:32.721,16:22:32.726,4,4444,16:22:32.726,0,36824,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2551,https://entitlement.auth.adobe.com/entitlement/v4/AccessEnabler.js,entitlement.auth.adobe.com,/entitlement/v4/AccessEnabler.js,Completed,200,GET,application/javascript,Google Chrome,127.0.0.1:54396,entitlement.auth.adobe.com - 23.42.82.205:443,16:22:28.282,16:22:28.732,451,16:22:28.748,16:22:28.779,31,498,16:22:28.779,0,131981,0,39090,,entitlement.auth.adobe.com - 23.42.82.205:443,ssl.adobe.com,ssl.adobe.com; signcsa.experienceleague.stage.adobe.com; wwwimages.stage2.adobe.com; www.macromedia.com; www.adobeidealab.com; cdn-stg-ffc.oobesaas.adobe.com; solutionpartners.stage2.adobe.com; www-stage01.acrobat.adobe.com; updates.adobeleanprint.com; download.stage.adobeprerelease.com; store2.stage2.adobe.com; live.adobeprimetime.com; wwwimages2.stage2.adobe.com; adobeprerelease.com; exchange.adobe.com; stage.status.adobe.com; experiencecloud.adobeexchange.com; www.stage.macromedia.com; experiencecloudstg2.adobeexchange.com; www.stage.adobeprerelease.com; www.qa.adobeprerelease.com; fpdownload.macromedia.com; stage.adobeprerelease.com; plan.adobe.com; dev.status.adobe.com; static.stage.adobeprimetime.com; preprod.status.adobe.com; live.stage.adobeprimetime.com; www.adobe.io; qe-ffc-static-cdn.oobesaas.adobe.com; download.adobeprerelease.com; trainingpartners.stage2.adobe.com; www.adobe-students.com; store1.stage2.adobe.com; auth.adobefpl.com; partners.stage2.adobe.com; media.stage.adobeprimetime.com; stage.adobe.io; cdn-qe-ffc.oobesaas.adobe.com; geo2.adobe.com; media.adobeprimetime.com; ffc-static-cdn.oobesaas.adobe.com; data.status.adobe.com; updates.stage.adobeleanprint.com; static.adobeprimetime.com; www.adobeprerelease.com; signcsa.experienceleague.adobe.com; hendrix360.qe.adobe.com; download.macromedia.com; csa.experienceleague.stage.adobe.com; app-cdn.stage2.adobe.com; stage.plan.adobe.com; www.stage2.adobe.com; stage.exchange.adobe.com; csa.experienceleague.adobe.com; entitlement.auth.adobe.com; download.qa.adobeprerelease.com; channelpartners.stage2.adobe.com; cdn-ffc.oobesaas.adobe.com; stg-ffc-static-cdn.oobesaas.adobe.com; technologypartners.stage2.adobe.com; www.stage.adobe-students.com,
|
||||
2550,https://www.nbc.com/generetic/manifest.json,www.nbc.com,/generetic/manifest.json,Completed,200,GET,application/json,Google Chrome,127.0.0.1:54397,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.281,16:22:28.894,612,16:22:28.996,16:22:28.996,0,715,16:22:28.996,0,205,0,170,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2553,https://img.nbc.com/files/2024-08/nbcsports_white_logo-425x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/2024-08/nbcsports_white_logo-425x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54395,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.279,16:22:32.670,4391,16:22:32.711,16:22:32.711,0,4432,16:22:32.711,0,12136,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2552,"https://img.nbc.com/files/2026-03/pickens-2026-web-dynamiclead-desktop-1920x1080.png?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/pickens-2026-web-dynamiclead-desktop-1920x1080.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54394,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.279,16:22:29.572,1293,16:22:29.632,16:22:29.681,49,1402,16:22:29.681,0,110928,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2551,https://img.nbc.com/files/2025-11/nba-coast-2-coast-logo.png?impolicy=nbc_com&imwidth=1260&imdensity=1,img.nbc.com,/files/2025-11/nba-coast-2-coast-logo.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54393,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.278,16:22:29.576,1298,16:22:29.664,16:22:29.666,2,1388,16:22:29.666,0,15998,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2544,https://www.nbc.com/generetic/generated/fonts/AcuminPro_600.woff2,www.nbc.com,/generetic/generated/fonts/AcuminPro_600.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.267,16:22:28.267,0,16:22:28.369,16:22:28.371,2,104,16:22:28.371,0,47300,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2543,https://www.nbc.com/generetic/generated/fonts/AcuminPro_400.woff2,www.nbc.com,/generetic/generated/fonts/AcuminPro_400.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54376,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.102,16:22:28.889,787,16:22:28.996,16:22:28.996,0,894,16:22:28.996,0,46872,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2542,https://www.nbc.com/generetic/generated/fonts/AcuminPro_200.woff2,www.nbc.com,/generetic/generated/fonts/AcuminPro_200.woff2,Completed,200,GET,font/woff2,Google Chrome,127.0.0.1:54375,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.102,16:22:28.345,242,16:22:28.471,16:22:28.471,0,368,16:22:28.471,0,46112,0,0,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2541,https://www.nbc.com/generetic/generated/generetic.47e9e8df145539694627.js,www.nbc.com,/generetic/generated/generetic.47e9e8df145539694627.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54378,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.102,16:22:28.349,247,16:22:28.470,16:22:28.540,69,437,16:22:28.540,0,1691217,0,281277,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2540,https://img.nbc.com/files/images/2023/1/05/NBC-Logo-Stacked-White-318x300.png?impolicy=nbc_com&imwidth=340&imdensity=1,img.nbc.com,/files/images/2023/1/05/NBC-Logo-Stacked-White-318x300.png,Completed,200,GET,image/webp,Google Chrome,127.0.0.1:54381,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.102,16:22:28.481,379,16:22:28.525,16:22:28.525,0,423,16:22:28.525,0,7104,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2539,"https://img.nbc.com/files/2026-03/nba25-26_wk24_tue_nyk-hou_2048x1152-sm-dynamic-lead-mobile-web.jpg?impolicy=nbc_com&im=Resize,width=1050;Crop,width=1050,height=472,gravity=NorthEast",img.nbc.com,/files/2026-03/nba25-26_wk24_tue_nyk-hou_2048x1152-sm-dynamic-lead-mobile-web.jpg,Completed,200,GET,image/jpeg,Google Chrome,127.0.0.1:54380,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,16:22:28.102,16:22:28.473,371,16:22:28.549,16:22:28.582,33,480,16:22:28.582,0,90154,0,0,,img.nbc.com - 2600:1406:4e00:19::1738:6d49:443,*.nbc.com,*.nbc.com; nbc.com; img.nbc.com,
|
||||
2538,https://www.nbc.com/generetic/generated/generetic.4de6b8d2e256b882e8f3.js,www.nbc.com,/generetic/generated/generetic.4de6b8d2e256b882e8f3.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54377,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.102,16:22:28.880,779,16:22:28.921,16:22:29.063,142,962,16:22:29.063,0,2877978,0,636470,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2543,https://www.nbc.com/generetic/generated/generetic.795899cb87676149668c.js,www.nbc.com,/generetic/generated/generetic.795899cb87676149668c.js,Completed,200,GET,application/javascript; charset=UTF-8,Google Chrome,127.0.0.1:54373,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.094,16:22:28.881,787,16:22:29.001,16:22:29.002,0,907,16:22:29.002,0,3227,0,1215,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2537,https://www.nbc.com/generetic/generated/generetic.aee12a093cbf23fc187f.css,www.nbc.com,/generetic/generated/generetic.aee12a093cbf23fc187f.css,Completed,200,GET,text/css; charset=UTF-8,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:28.089,16:22:28.089,0,16:22:28.254,16:22:28.267,13,177,16:22:28.267,0,841561,0,133459,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2532,https://www.nbc.com/sports,www.nbc.com,/sports,Completed,200,GET,text/html; charset=utf-8,Google Chrome,127.0.0.1:54365,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,16:22:26.024,16:22:26.124,99,16:22:28.071,16:22:28.089,18,2065,16:22:28.089,0,665910,0,71508,,www.nbc.com - 2600:1406:4e00:19::1738:6d4c:443,*.nbc.com,*.nbc.com; nbc.com; www.nbc.com,
|
||||
2531,https://nbcsports.app.link/stream_Phillies_NBCSPhilly,nbcsports.app.link,/stream_Phillies_NBCSPhilly,Redirect,307,GET,,Google Chrome,127.0.0.1:54357,nbcsports.app.link - 2600:9000:24da:5000:19:9934:6a80:93a1:443,16:22:25.951,16:22:25.951,0,16:22:26.012,16:22:26.012,0,61,16:22:26.012,0,0,0,0,,nbcsports.app.link - 2600:9000:24da:5000:19:9934:6a80:93a1:443,appipv4.link,appipv4.link; app.link; *.app.link; *.appipv4.link; nbcsports.app.link,
|
||||
2530,https://nbcsports.app.link/stream_Phillies_NBCSPhilly,nbcsports.app.link,/stream_Phillies_NBCSPhilly,Redirect,307,GET,text/html,Google Chrome,127.0.0.1:54357,nbcsports.app.link - 2600:9000:24da:5000:19:9934:6a80:93a1:443,16:22:25.737,16:22:25.901,163,16:22:25.948,16:22:25.948,0,211,16:22:25.948,0,54,0,0,,nbcsports.app.link - 2600:9000:24da:5000:19:9934:6a80:93a1:443,appipv4.link,appipv4.link; app.link; *.app.link; *.appipv4.link; nbcsports.app.link,
|
||||
2531,https://nbcu.demdex.net/event?d_dil_ver=9.5&_ts=1774999345725,nbcu.demdex.net,/event,Completed,200,POST,application/json;charset=utf-8,Google Chrome,127.0.0.1:54353,nbcu.demdex.net - 54.71.255.99:443,16:22:25.734,16:22:25.971,238,16:22:26.036,16:22:26.036,0,302,16:22:26.036,2340,2901,0,982,,nbcu.demdex.net - 54.71.255.99:443,*.demdex.com,*.demdex.com; tableau.aam.adobe.com; audiencemanager.adobe.com; *.demdex.net; demdex.net; *.audiencemanager.adobe.com; *.aam.adobe.com; tableau.demdex.com; demdex.com; audience-manager.adobe.com; aam.adobe.com; *.audience-manager.adobe.com; nbcu.demdex.net,
|
||||
2530,https://nbcume.sc.omtrdc.net/b/ss/nbcursndivisiontotal/1/JS-2.22.3-LEWM/s19040561078154?AQB=1&ndh=1&pf=1&t=31%2F2%2F2026%2016%3A22%3A25%202%20420&mid=48149800880344474781461921042733640603&aamlh=9&ce=UTF-8&pageName=streaming-faqs-phillies%3Astreaming-faqs-phillies%20landing%3Asection%20landing%20page&g=https%3A%2F%2Fwww.nbcsportsphiladelphia.com%2Fstreaming-faqs-phillies%2F&cc=USD&ch=streaming-faqs-phillies&server=nbcsportsphiladelphia&events=event1&c1=streaming-faqs-phillies&v1=D%3Dc1&c2=form%20landing&v3=D%3Dc3&v4=D%3Dc4&v5=streaming-faqs-phillies%3Astreaming-faqs-phillies%20landing%3Asection%20landing%20page&c6=https%3A%2F%2Fwww.nbcsportsphiladelphia.com%2Fstreaming-faqs-phillies%2F&v6=D%3Dc6&c8=rsn&v8=D%3Dc8&c9=RSN-PHI&v9=D%3Dc9&c10=NSPH&v10=D%3Dc10&v12=https%3A%2F%2Fwww.nbcsportsphiladelphia.com%2Fstreaming-faqs-phillies%2F&v13=D%3Dc13&v15=D%3Dc15&v16=D%3Dc16&v17=D%3Dc17&c19=false&c20=nbcursndivisiontotal&v21=D%3Dc21&v22=D%3Dc22&c23=form&v23=D%3Dc23&c25=d%3Dc18&v25=D%3Dc25&v28=D%3Dc28&v29=D%3Dc29&c32=no%20keyword&v32=D%3Dc32&c48=desktop&c49=Phillies%20Streaming%20%E2%80%93%20NBC%20Sports%20Philadelphia&v49=D%3Dc49&v54=nbcsportsphiladelphia&v55=streaming-faqs-phillies&v57=D%3Dc57&c59=feb%2006%2C%202026%2022%3A10%3A10%20pm%20est&v59=D%3Dc59&v68=D%3Dc68&c70=may%2010%2C%202023%2016%3A05%3A16%20pm%20edt&v70=D%3Dc70&c74=page%20not%20sponsored&v74=D%3Dc74&v75=D%3Dc75&v76=page%20not%20sponsored&v80=D%3Dc2&pe=lnk_e&pev1=https%3A%2F%2Fnbcsports.app.link%2Fstream_Phillies_NBCSPhilly&c.&a.&activitymap.&page=streaming-faqs-phillies%3Astreaming-faqs-phillies%20landing%3Asection%20landing%20page&link=right%20here®ion=post-219153&pageIDType=1&.activitymap&.a&.c&pid=streaming-faqs-phillies%3Astreaming-faqs-phillies%20landing%3Asection%20landing%20page&pidt=1&oid=https%3A%2F%2Fnbcsports.app.link%2Fstream_Phillies_NBCSPhilly&ot=A&s=1800x1169&c=30&j=1.6&v=N&k=Y&bw=1512&bh=862&mcorgid=A8AB776A5245B4220A490D44%40AdobeOrg&AQE=1,nbcume.sc.omtrdc.net,/b/ss/nbcursndivisiontotal/1/JS-2.22.3-LEWM/s19040561078154,Completed,200,POST,image/gif;charset=utf-8,Google Chrome,127.0.0.1:54352,nbcume.sc.omtrdc.net - 63.140.37.172:443,16:22:25.734,16:22:25.947,213,16:22:25.999,16:22:25.999,0,265,16:22:25.999,0,43,0,0,,nbcume.sc.omtrdc.net - 63.140.37.172:443,*.sc.omtrdc.net,*.sc.omtrdc.net; nbcume.sc.omtrdc.net,
|
||||
|
1
intake/soup.har
Normal file
1
intake/soup.har
Normal file
File diff suppressed because one or more lines are too long
6
justfile
6
justfile
|
|
@ -12,9 +12,15 @@ git-hosting:
|
|||
git-mirrors:
|
||||
./scripts/git-push-mirrors.sh
|
||||
|
||||
deploy-ecp-forge:
|
||||
./scripts/deploy-ecp-forge.sh
|
||||
|
||||
netboot-stage:
|
||||
./scripts/netboot-stage.sh
|
||||
|
||||
netboot-build-ipxe:
|
||||
./scripts/netboot-build-ipxe.sh
|
||||
|
||||
netboot-serve:
|
||||
./scripts/netboot-serve.sh
|
||||
|
||||
|
|
|
|||
377
nix/modules/ec-ipxe-qemu.nix
Normal file
377
nix/modules/ec-ipxe-qemu.nix
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.every-channel.ipxe-qemu;
|
||||
scriptsRoot = ../..;
|
||||
|
||||
firstDns = lib.head cfg.boot.userNet.dnsServers;
|
||||
qemuBin = "${cfg.package}/bin/qemu-system-x86_64";
|
||||
qemuImgBin = "${cfg.package}/bin/qemu-img";
|
||||
bootNetdevArg = lib.escapeShellArg "user,id=boot0,dns=${firstDns},hostname=${cfg.boot.userNet.hostname},tftp=${cfg.boot.userNet.tftpDir},bootfile=${cfg.boot.userNet.bootFilename}";
|
||||
bootDeviceArg = lib.escapeShellArg "${cfg.boot.userNet.model},netdev=boot0";
|
||||
lanNetdevArg = lib.escapeShellArg "tap,id=lan0,fd=3";
|
||||
lanDeviceArg = lib.escapeShellArg "${cfg.lan.model},netdev=lan0";
|
||||
driveArg = lib.escapeShellArg "if=virtio,format=qcow2,file=${cfg.disk.path}";
|
||||
effectiveChainUrl =
|
||||
if cfg.boot.chainUrl != null
|
||||
then cfg.boot.chainUrl
|
||||
else "http://10.0.2.2:${toString cfg.boot.http.port}/netboot.ipxe";
|
||||
|
||||
bootScript = pkgs.writeText "every-channel-qemu.ipxe" ''
|
||||
#!ipxe
|
||||
dhcp
|
||||
set dns ${firstDns}
|
||||
chain ${effectiveChainUrl} || shell
|
||||
'';
|
||||
bootAsset = pkgs.ipxe.override {
|
||||
embedScript = bootScript;
|
||||
additionalTargets = {
|
||||
"bin/undionly.kpxe" = null;
|
||||
};
|
||||
firmwareBinary = "undionly.kpxe";
|
||||
};
|
||||
|
||||
bootExtraArgs = lib.concatMapStringsSep "\n" (arg: " cmd+=(${lib.escapeShellArg arg})") cfg.extraArgs;
|
||||
|
||||
runner = pkgs.writeShellApplication {
|
||||
name = "every-channel-ipxe-qemu";
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.iproute2
|
||||
cfg.package
|
||||
];
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
|
||||
state_dir=${lib.escapeShellArg cfg.stateDir}
|
||||
tftp_dir=${lib.escapeShellArg cfg.boot.userNet.tftpDir}
|
||||
boot_file=${lib.escapeShellArg cfg.boot.userNet.bootFilename}
|
||||
disk_path=${lib.escapeShellArg cfg.disk.path}
|
||||
disk_size=${lib.escapeShellArg cfg.disk.size}
|
||||
lan_ifname=${lib.escapeShellArg cfg.lan.macvtap.name}
|
||||
enable_kvm=${lib.escapeShellArg (lib.boolToString cfg.enableKvm)}
|
||||
enable_lan=${lib.escapeShellArg (lib.boolToString cfg.lan.enable)}
|
||||
serve_http=${lib.escapeShellArg (lib.boolToString cfg.boot.http.enable)}
|
||||
http_bind_ip=${lib.escapeShellArg cfg.boot.http.bindIp}
|
||||
http_port=${toString cfg.boot.http.port}
|
||||
http_root=${lib.escapeShellArg cfg.boot.http.root}
|
||||
|
||||
cleanup() {
|
||||
if [[ "$enable_lan" == true ]]; then
|
||||
ip link del "$lan_ifname" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
terminate() {
|
||||
if [[ -n "''${http_pid:-}" ]]; then
|
||||
kill "$http_pid" 2>/dev/null || true
|
||||
wait "$http_pid" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -n "''${qemu_pid:-}" ]]; then
|
||||
kill "$qemu_pid" 2>/dev/null || true
|
||||
wait "$qemu_pid" 2>/dev/null || true
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap terminate TERM INT
|
||||
|
||||
install -d -m 0755 "$state_dir" "$tftp_dir" "$(dirname "$disk_path")"
|
||||
install -m 0644 ${bootAsset}/undionly.kpxe "$tftp_dir/$boot_file"
|
||||
|
||||
if [[ "$serve_http" == true ]]; then
|
||||
if [[ ! -d "$http_root" ]]; then
|
||||
echo "error: boot HTTP root not found: $http_root" >&2
|
||||
exit 1
|
||||
fi
|
||||
${pkgs.python3}/bin/python3 ${scriptsRoot}/scripts/netboot-http-server.py \
|
||||
--bind-ip "$http_bind_ip" \
|
||||
--port "$http_port" \
|
||||
--root "$http_root" &
|
||||
http_pid=$!
|
||||
fi
|
||||
|
||||
if [[ ! -f "$disk_path" ]]; then
|
||||
${qemuImgBin} create -f qcow2 "$disk_path" "$disk_size" >/dev/null
|
||||
fi
|
||||
|
||||
cmd=(
|
||||
${lib.escapeShellArg qemuBin}
|
||||
-name ${lib.escapeShellArg cfg.name}
|
||||
-machine ${lib.escapeShellArg cfg.machine}
|
||||
-cpu ${lib.escapeShellArg cfg.cpu}
|
||||
-smp ${toString cfg.vcpus}
|
||||
-m ${toString cfg.memoryMiB}
|
||||
-nographic
|
||||
-boot ${lib.escapeShellArg cfg.boot.order}
|
||||
-serial ${lib.escapeShellArg cfg.serial}
|
||||
-device virtio-rng-pci
|
||||
)
|
||||
|
||||
if [[ "$enable_kvm" == true ]]; then
|
||||
cmd+=(-enable-kvm)
|
||||
fi
|
||||
|
||||
cmd+=(
|
||||
-netdev ${bootNetdevArg}
|
||||
-device ${bootDeviceArg}
|
||||
)
|
||||
|
||||
if [[ "$enable_lan" == true ]]; then
|
||||
ip link del "$lan_ifname" 2>/dev/null || true
|
||||
ip link add link ${lib.escapeShellArg cfg.lan.macvtap.interface} name "$lan_ifname" type macvtap mode ${lib.escapeShellArg cfg.lan.macvtap.mode}
|
||||
ip link set "$lan_ifname" up
|
||||
tap_index="$(< /sys/class/net/$lan_ifname/ifindex)"
|
||||
exec 3<>"/dev/tap''${tap_index}"
|
||||
cmd+=(
|
||||
-netdev ${lanNetdevArg}
|
||||
-device ${lanDeviceArg}
|
||||
)
|
||||
fi
|
||||
|
||||
cmd+=(
|
||||
-drive ${driveArg}
|
||||
)
|
||||
${bootExtraArgs}
|
||||
|
||||
"''${cmd[@]}" &
|
||||
qemu_pid=$!
|
||||
wait "$qemu_pid"
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.services.every-channel.ipxe-qemu = {
|
||||
enable = lib.mkEnableOption "every.channel iPXE/QEMU boot VM";
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.qemu_kvm;
|
||||
description = "QEMU package providing qemu-system-x86_64 and qemu-img.";
|
||||
};
|
||||
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "every-channel-ipxe";
|
||||
description = "QEMU guest name.";
|
||||
};
|
||||
|
||||
machine = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "q35,accel=kvm";
|
||||
description = "QEMU machine string.";
|
||||
};
|
||||
|
||||
cpu = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "host";
|
||||
description = "QEMU CPU model.";
|
||||
};
|
||||
|
||||
enableKvm = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Use KVM acceleration when available.";
|
||||
};
|
||||
|
||||
vcpus = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
default = 2;
|
||||
description = "Virtual CPU count.";
|
||||
};
|
||||
|
||||
memoryMiB = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
default = 4096;
|
||||
description = "Guest memory in MiB.";
|
||||
};
|
||||
|
||||
serial = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "mon:stdio";
|
||||
description = "Serial/monitor wiring passed to QEMU.";
|
||||
};
|
||||
|
||||
stateDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/every-channel/ipxe-qemu";
|
||||
description = "Persistent state directory for qcow2 disk and boot assets.";
|
||||
};
|
||||
|
||||
disk = {
|
||||
path = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${cfg.stateDir}/disk.qcow2";
|
||||
description = "Persistent qcow2 disk path.";
|
||||
};
|
||||
|
||||
size = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "16G";
|
||||
description = "Size used when initializing the qcow2 disk.";
|
||||
};
|
||||
};
|
||||
|
||||
boot = {
|
||||
order = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "order=n";
|
||||
description = "QEMU boot order, defaulting to network first.";
|
||||
};
|
||||
|
||||
chainUrl = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional explicit iPXE chain target fetched by the guest; defaults to the local boot.http server.";
|
||||
};
|
||||
|
||||
http = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Serve a host-local HTTP root for the guest netboot flow.";
|
||||
};
|
||||
|
||||
bindIp = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Bind address used for the host-local boot HTTP server.";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
description = "Port used for the host-local boot HTTP server.";
|
||||
};
|
||||
|
||||
root = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Directory containing kernel/initrd/netboot.ipxe served to the guest.";
|
||||
};
|
||||
};
|
||||
|
||||
userNet = {
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "every-channel-ipxe";
|
||||
description = "Hostname advertised on the user-mode boot network.";
|
||||
};
|
||||
|
||||
dnsServers = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "1.1.1.1" ];
|
||||
description = "DNS servers used by the user-mode boot NIC; the first entry is applied.";
|
||||
};
|
||||
|
||||
tftpDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${cfg.stateDir}/tftp";
|
||||
description = "TFTP directory used by the user-mode boot NIC.";
|
||||
};
|
||||
|
||||
bootFilename = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "undionly.kpxe";
|
||||
description = "Boot asset filename exposed on the user-mode boot NIC.";
|
||||
};
|
||||
|
||||
model = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "e1000";
|
||||
description = "NIC model used for the boot network.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lan = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Attach a second NIC to a real LAN using macvtap.";
|
||||
};
|
||||
|
||||
model = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "virtio-net-pci";
|
||||
description = "NIC model used for the LAN attachment.";
|
||||
};
|
||||
|
||||
macvtap = {
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Host interface used as the lower device for macvtap.";
|
||||
};
|
||||
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "ecqemu0";
|
||||
description = "macvtap link name created while the guest runs.";
|
||||
};
|
||||
|
||||
mode = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"private"
|
||||
"vepa"
|
||||
"bridge"
|
||||
"passthru"
|
||||
"source"
|
||||
];
|
||||
default = "bridge";
|
||||
description = "macvtap mode used for the LAN attachment.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Additional arguments appended to the QEMU command.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.boot.userNet.dnsServers != [ ];
|
||||
message = "services.every-channel.ipxe-qemu.boot.userNet.dnsServers must not be empty";
|
||||
}
|
||||
{
|
||||
assertion = (!cfg.lan.enable) || (cfg.lan.macvtap.interface != null);
|
||||
message = "services.every-channel.ipxe-qemu.lan.macvtap.interface must be set when lan.enable is true";
|
||||
}
|
||||
{
|
||||
assertion = (!cfg.boot.http.enable) || (cfg.boot.http.root != null);
|
||||
message = "services.every-channel.ipxe-qemu.boot.http.root must be set when boot.http.enable is true";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ runner ];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.stateDir} 0755 root root -"
|
||||
"d ${cfg.boot.userNet.tftpDir} 0755 root root -"
|
||||
];
|
||||
|
||||
systemd.services.every-channel-ipxe-qemu = {
|
||||
description = "every.channel iPXE QEMU VM";
|
||||
after = [ "local-fs.target" "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [
|
||||
pkgs.coreutils
|
||||
pkgs.iproute2
|
||||
cfg.package
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${runner}/bin/every-channel-ipxe-qemu";
|
||||
Restart = "always";
|
||||
RestartSec = 5;
|
||||
WorkingDirectory = cfg.stateDir;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
338
nix/modules/ec-netboot.nix
Normal file
338
nix/modules/ec-netboot.nix
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.every-channel.netboot;
|
||||
scriptsRoot = ../..;
|
||||
|
||||
boolString = v: if v then "true" else "false";
|
||||
|
||||
mkExport = name: value: "export ${name}=${lib.escapeShellArg value}";
|
||||
|
||||
optionalExport = name: value:
|
||||
if value == null then "" else mkExport name value;
|
||||
|
||||
optionalFileExport = name: path:
|
||||
if path == null then ""
|
||||
else ''
|
||||
if [[ ! -r ${lib.escapeShellArg path} ]]; then
|
||||
echo "error: required file is not readable: ${path}" >&2
|
||||
exit 2
|
||||
fi
|
||||
export ${name}="$(tr -d '\\r\\n' < ${lib.escapeShellArg path})"
|
||||
'';
|
||||
|
||||
stageToolchain = with pkgs; [
|
||||
bash
|
||||
coreutils
|
||||
curl
|
||||
gawk
|
||||
gnugrep
|
||||
gnused
|
||||
gnutar
|
||||
gzip
|
||||
python3
|
||||
];
|
||||
|
||||
ipxeToolchain = with pkgs; [
|
||||
git
|
||||
gnumake
|
||||
gcc
|
||||
binutils
|
||||
perl
|
||||
mtools
|
||||
docker-client
|
||||
];
|
||||
in
|
||||
{
|
||||
options.services.every-channel.netboot = {
|
||||
enable = lib.mkEnableOption "every.channel persistent netboot stage/serve services";
|
||||
|
||||
listenIP = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "10.20.30.2";
|
||||
description = "IP address bound for TFTP/HTTP on the provisioning VLAN.";
|
||||
};
|
||||
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "enp3s0";
|
||||
description = "Network interface name on the provisioning VLAN.";
|
||||
};
|
||||
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "boot.every.channel";
|
||||
description = "Boot server hostname advertised to DHCP/iPXE clients.";
|
||||
};
|
||||
|
||||
rootDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/every-channel-netboot";
|
||||
description = "Persistent root directory containing staged HTTP and TFTP artifacts.";
|
||||
};
|
||||
|
||||
httpPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
description = "HTTP port used for kernel/initrd/netboot script serving.";
|
||||
};
|
||||
|
||||
tftpBootFilename = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "ec-ipxe.efi";
|
||||
description = "Filename served via TFTP (DHCP option 67 in UniFi-only mode).";
|
||||
};
|
||||
|
||||
chainTokenFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "/run/agenix/every-channel-netboot-chain-token";
|
||||
description = "Optional file containing netboot chain token passed to stage/serve scripts.";
|
||||
};
|
||||
|
||||
httpAllowedCIDRs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "10.20.30.0/24" ];
|
||||
description = "Optional CIDR allowlist for HTTP artifact serving.";
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Open firewall ports for TFTP/HTTP (and ProxyDHCP ports when enabled).";
|
||||
};
|
||||
|
||||
stageOnBoot = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Start the stage oneshot service during boot before serving.";
|
||||
};
|
||||
|
||||
release = {
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://git.every.channel";
|
||||
description = "Forge host used to fetch netboot release assets.";
|
||||
};
|
||||
|
||||
repo = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "every-channel/every.channel";
|
||||
description = "Forge repository containing netboot release assets.";
|
||||
};
|
||||
|
||||
tag = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional release tag to stage; defaults to latest release.";
|
||||
};
|
||||
|
||||
localTarball = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional local netboot tarball path; bypasses release API download when set.";
|
||||
};
|
||||
|
||||
tokenFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional file containing Forge API token for private release access.";
|
||||
};
|
||||
|
||||
verifyChecksums = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Verify staged release tarball using SHA256SUMS.txt when available.";
|
||||
};
|
||||
};
|
||||
|
||||
ipxe = {
|
||||
buildEmbedded = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Build embedded iPXE (ec-ipxe.efi) before staging artifacts.";
|
||||
};
|
||||
|
||||
path = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional prebuilt iPXE EFI binary path; used when buildEmbedded is false.";
|
||||
};
|
||||
|
||||
allowRemoteDownload = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Allow remote iPXE URL download during staging. Disabled by default.";
|
||||
};
|
||||
|
||||
sha256 = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional SHA256 digest expected for selected iPXE EFI binary.";
|
||||
};
|
||||
|
||||
repo = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://github.com/ipxe/ipxe.git";
|
||||
description = "iPXE source repository used for embedded EFI builds.";
|
||||
};
|
||||
|
||||
ref = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Optional git ref/commit for iPXE build reproducibility.";
|
||||
};
|
||||
|
||||
useDocker = lib.mkOption {
|
||||
type = lib.types.enum [ "auto" "true" "false" ];
|
||||
default = "auto";
|
||||
description = "Container fallback mode for iPXE builds (auto/true/false).";
|
||||
};
|
||||
|
||||
dockerImage = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "ubuntu:24.04";
|
||||
description = "Docker image used when iPXE build runs in container mode.";
|
||||
};
|
||||
|
||||
dockerPlatform = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "linux/amd64";
|
||||
description = "Docker platform used for containerized iPXE build.";
|
||||
};
|
||||
};
|
||||
|
||||
proxyDhcp = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Enable dnsmasq ProxyDHCP chainloading mode.";
|
||||
};
|
||||
|
||||
subnet = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "10.20.30.0/24";
|
||||
description = "ProxyDHCP subnet (required when proxyDhcp.enable is true).";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.proxyDhcp.enable -> cfg.proxyDhcp.subnet != null;
|
||||
message = "services.every-channel.netboot.proxyDhcp.subnet must be set when proxyDhcp.enable is true";
|
||||
}
|
||||
{
|
||||
assertion = cfg.ipxe.buildEmbedded || cfg.ipxe.path != null || cfg.ipxe.allowRemoteDownload;
|
||||
message = "Set ipxe.buildEmbedded=true, or provide ipxe.path, or allow ipxe.allowRemoteDownload=true";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.rootDir} 0750 root root -"
|
||||
"d ${cfg.rootDir}/http 0750 root root -"
|
||||
"d ${cfg.rootDir}/tftp 0750 root root -"
|
||||
];
|
||||
|
||||
systemd.services.every-channel-netboot-ipxe = lib.mkIf cfg.ipxe.buildEmbedded {
|
||||
description = "every.channel netboot embedded iPXE build";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = lib.mkIf cfg.stageOnBoot [ "multi-user.target" ];
|
||||
path = stageToolchain ++ ipxeToolchain;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_ROOT" cfg.rootDir}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HOSTNAME" cfg.hostname}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HTTP_PORT" (toString cfg.httpPort)}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_IPXE_FILENAME" cfg.tftpBootFilename}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_IPXE_REPO" cfg.ipxe.repo}
|
||||
${optionalExport "EVERY_CHANNEL_NETBOOT_IPXE_REF" cfg.ipxe.ref}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_IPXE_USE_DOCKER" cfg.ipxe.useDocker}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_IMAGE" cfg.ipxe.dockerImage}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_PLATFORM" cfg.ipxe.dockerPlatform}
|
||||
${optionalFileExport "EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN" cfg.chainTokenFile}
|
||||
|
||||
${scriptsRoot}/scripts/netboot-build-ipxe.sh
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.every-channel-netboot-stage = {
|
||||
description = "every.channel netboot artifact stage";
|
||||
requires = lib.optionals cfg.ipxe.buildEmbedded [ "every-channel-netboot-ipxe.service" ];
|
||||
after = [ "network-online.target" ] ++ lib.optionals cfg.ipxe.buildEmbedded [ "every-channel-netboot-ipxe.service" ];
|
||||
wants = [ "network-online.target" ] ++ lib.optionals cfg.ipxe.buildEmbedded [ "every-channel-netboot-ipxe.service" ];
|
||||
wantedBy = lib.mkIf cfg.stageOnBoot [ "multi-user.target" ];
|
||||
path = stageToolchain;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_ROOT" cfg.rootDir}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HOSTNAME" cfg.hostname}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HTTP_PORT" (toString cfg.httpPort)}
|
||||
${mkExport "EVERY_CHANNEL_FORGE_HOST" cfg.release.host}
|
||||
${mkExport "EVERY_CHANNEL_FORGE_REPO" cfg.release.repo}
|
||||
${optionalExport "EVERY_CHANNEL_NETBOOT_RELEASE_TAG" cfg.release.tag}
|
||||
${optionalExport "EVERY_CHANNEL_NETBOOT_TARBALL" cfg.release.localTarball}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_VERIFY_RELEASE_CHECKSUMS" (boolString cfg.release.verifyChecksums)}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_ALLOW_REMOTE_IPXE" (boolString cfg.ipxe.allowRemoteDownload)}
|
||||
${mkExport "EVERY_CHANNEL_IPXE_EFI_FILENAME" cfg.tftpBootFilename}
|
||||
${optionalExport "EVERY_CHANNEL_IPXE_EFI_SHA256" cfg.ipxe.sha256}
|
||||
${optionalFileExport "EVERY_CHANNEL_FORGE_TOKEN" cfg.release.tokenFile}
|
||||
${optionalFileExport "EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN" cfg.chainTokenFile}
|
||||
|
||||
${lib.optionalString cfg.ipxe.buildEmbedded (mkExport "EVERY_CHANNEL_IPXE_EFI_PATH" "${cfg.rootDir}/tftp/${cfg.tftpBootFilename}")}
|
||||
${lib.optionalString (!cfg.ipxe.buildEmbedded && cfg.ipxe.path != null) (mkExport "EVERY_CHANNEL_IPXE_EFI_PATH" cfg.ipxe.path)}
|
||||
|
||||
${scriptsRoot}/scripts/netboot-stage.sh
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.every-channel-netboot = {
|
||||
description = "every.channel netboot HTTP + TFTP service";
|
||||
requires = [ "every-channel-netboot-stage.service" ];
|
||||
after = [ "network-online.target" "every-channel-netboot-stage.service" ];
|
||||
wants = [ "network-online.target" "every-channel-netboot-stage.service" ];
|
||||
wantedBy = lib.mkIf cfg.stageOnBoot [ "multi-user.target" ];
|
||||
path = stageToolchain ++ [ pkgs.dnsmasq ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_ROOT" cfg.rootDir}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_LISTEN_IP" cfg.listenIP}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_INTERFACE" cfg.interface}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HOSTNAME" cfg.hostname}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HTTP_PORT" (toString cfg.httpPort)}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_PROXY_DHCP" (boolString cfg.proxyDhcp.enable)}
|
||||
${optionalExport "EVERY_CHANNEL_NETBOOT_PROXY_SUBNET" cfg.proxyDhcp.subnet}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME" cfg.tftpBootFilename}
|
||||
${mkExport "EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS" (lib.concatStringsSep "," cfg.httpAllowedCIDRs)}
|
||||
${optionalFileExport "EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN" cfg.chainTokenFile}
|
||||
|
||||
${scriptsRoot}/scripts/netboot-serve.sh
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.httpPort ];
|
||||
networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall (
|
||||
[ 69 ] ++ lib.optionals cfg.proxyDhcp.enable [ 67 4011 ]
|
||||
);
|
||||
};
|
||||
}
|
||||
387
nix/modules/ec-op-stack.nix
Normal file
387
nix/modules/ec-op-stack.nix
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.every-channel.op-stack;
|
||||
scriptRoot = ../..;
|
||||
bootstrapScript = "${scriptRoot}/scripts/op-stack/setup-rollup.sh";
|
||||
downloadScript = "${scriptRoot}/scripts/op-stack/download-op-deployer.sh";
|
||||
|
||||
containerName = name: "every-channel-op-${name}";
|
||||
in
|
||||
{
|
||||
options.services.every-channel.op-stack = {
|
||||
enable = lib.mkEnableOption "every.channel OP Stack Sepolia testnet services";
|
||||
|
||||
rootDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/every-channel/op-stack";
|
||||
description = "Persistent root directory for OP Stack bootstrap outputs and container state.";
|
||||
};
|
||||
|
||||
privateKeyFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "File containing the Sepolia private key used for op-deployer and operator services.";
|
||||
};
|
||||
|
||||
l1RpcUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://ethereum-sepolia-rpc.publicnode.com";
|
||||
description = "Sepolia L1 RPC URL.";
|
||||
};
|
||||
|
||||
l1BeaconUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://ethereum-sepolia-beacon-api.publicnode.com";
|
||||
description = "Sepolia L1 beacon API URL.";
|
||||
};
|
||||
|
||||
chainId = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
default = 245245;
|
||||
description = "L2 chain ID for the every.channel OP Stack testnet.";
|
||||
};
|
||||
|
||||
p2pAdvertiseIp = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Public IP advertised by op-node for P2P.";
|
||||
};
|
||||
|
||||
p2pListenPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 9222;
|
||||
description = "P2P listen port for op-node.";
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Open the op-node P2P TCP/UDP port.";
|
||||
};
|
||||
|
||||
disputeMonEnable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Run op-dispute-mon alongside the core OP Stack services.";
|
||||
};
|
||||
|
||||
challengerEnable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Run op-challenger for the rollup.";
|
||||
};
|
||||
|
||||
challengerPrestateFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Path to the Cannon absolute prestate .bin.gz file used by op-challenger.";
|
||||
};
|
||||
|
||||
opDeployerTag = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "op-deployer/v0.6.0-rc.3";
|
||||
description = "Pinned op-deployer release tag used for bootstrap.";
|
||||
};
|
||||
|
||||
images = {
|
||||
opNode = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:v1.13.5";
|
||||
description = "Container image for op-node.";
|
||||
};
|
||||
opGeth = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.1";
|
||||
description = "Container image for op-geth.";
|
||||
};
|
||||
batcher = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-batcher:v1.14.0";
|
||||
description = "Container image for op-batcher.";
|
||||
};
|
||||
proposer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-proposer:v1.10.0";
|
||||
description = "Container image for op-proposer.";
|
||||
};
|
||||
challenger = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger:v1.5.1";
|
||||
description = "Container image for op-challenger.";
|
||||
};
|
||||
disputeMon = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-dispute-mon:v1.4.2-rc.1";
|
||||
description = "Container image for op-dispute-mon.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.privateKeyFile != null;
|
||||
message = "services.every-channel.op-stack.privateKeyFile must be set when the OP Stack is enabled";
|
||||
}
|
||||
{
|
||||
assertion = builtins.pathExists bootstrapScript;
|
||||
message = "missing bootstrap script at scripts/op-stack/setup-rollup.sh";
|
||||
}
|
||||
{
|
||||
assertion = builtins.pathExists downloadScript;
|
||||
message = "missing download helper at scripts/op-stack/download-op-deployer.sh";
|
||||
}
|
||||
{
|
||||
assertion = (!cfg.challengerEnable) || cfg.challengerPrestateFile != null;
|
||||
message = "services.every-channel.op-stack.challengerPrestateFile must be set when challengerEnable = true";
|
||||
}
|
||||
{
|
||||
assertion = (!cfg.disputeMonEnable) || cfg.challengerEnable;
|
||||
message = "services.every-channel.op-stack.disputeMonEnable requires challengerEnable = true";
|
||||
}
|
||||
];
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ cfg.p2pListenPort ];
|
||||
allowedUDPPorts = [ cfg.p2pListenPort ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.rootDir} 0750 root root - -"
|
||||
"d ${cfg.rootDir}/bin 0750 root root - -"
|
||||
"d ${cfg.rootDir}/deployer 0750 root root - -"
|
||||
"d ${cfg.rootDir}/sequencer 0750 root root - -"
|
||||
"d ${cfg.rootDir}/batcher 0750 root root - -"
|
||||
"d ${cfg.rootDir}/proposer 0750 root root - -"
|
||||
"d ${cfg.rootDir}/challenger 0750 root root - -"
|
||||
"d ${cfg.rootDir}/challenger/data 0750 root root - -"
|
||||
"d ${cfg.rootDir}/dispute-mon 0750 root root - -"
|
||||
"d ${cfg.rootDir}/op-geth-data 0750 root root - -"
|
||||
];
|
||||
|
||||
virtualisation.oci-containers.containers = {
|
||||
"${containerName "geth"}" = {
|
||||
image = cfg.images.opGeth;
|
||||
autoStart = true;
|
||||
volumes = [
|
||||
"${cfg.rootDir}/sequencer:/workspace"
|
||||
"${cfg.rootDir}/op-geth-data:/workspace/op-geth-data"
|
||||
];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
set -e
|
||||
if [ ! -d /workspace/op-geth-data/geth/chaindata ]; then
|
||||
geth init --datadir=/workspace/op-geth-data --state.scheme=hash /workspace/genesis.json
|
||||
fi
|
||||
exec geth --datadir=/workspace/op-geth-data --http --http.addr=127.0.0.1 --http.port=8545 --ws --ws.addr=127.0.0.1 --ws.port=8546 --authrpc.addr=127.0.0.1 --authrpc.port=8551 --authrpc.jwtsecret=/workspace/jwt.txt --syncmode=full --gcmode=archive --rollup.disabletxpoolgossip=true --http.vhosts=* --http.corsdomain=* --http.api=eth,net,web3,debug,txpool,admin --ws.origins=* --ws.api=eth,net,web3,debug,txpool,admin --authrpc.vhosts=*
|
||||
''
|
||||
];
|
||||
};
|
||||
|
||||
"${containerName "node"}" = {
|
||||
image = cfg.images.opNode;
|
||||
autoStart = true;
|
||||
volumes = [
|
||||
"${cfg.rootDir}/sequencer:/workspace"
|
||||
"${cfg.rootDir}/op-geth-data:/workspace/op-geth-data"
|
||||
];
|
||||
environmentFiles = [ "${cfg.rootDir}/sequencer/.env" ];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
exec op-node \
|
||||
--l1="$L1_RPC_URL" \
|
||||
--l1.beacon="$L1_BEACON_URL" \
|
||||
--l2=http://127.0.0.1:8551 \
|
||||
--l2.jwt-secret=/workspace/jwt.txt \
|
||||
--rollup.config=/workspace/rollup.json \
|
||||
--sequencer.enabled=true \
|
||||
--sequencer.stopped=false \
|
||||
--sequencer.max-safe-lag=3600 \
|
||||
--verifier.l1-confs=4 \
|
||||
--p2p.listen.ip=0.0.0.0 \
|
||||
--p2p.listen.tcp=${toString cfg.p2pListenPort} \
|
||||
--p2p.listen.udp=${toString cfg.p2pListenPort} \
|
||||
--p2p.advertise.ip="$P2P_ADVERTISE_IP" \
|
||||
--p2p.advertise.tcp=${toString cfg.p2pListenPort} \
|
||||
--p2p.advertise.udp=${toString cfg.p2pListenPort} \
|
||||
--p2p.sequencer.key="$PRIVATE_KEY" \
|
||||
--rpc.addr=127.0.0.1 \
|
||||
--rpc.port=8547 \
|
||||
--rpc.enable-admin \
|
||||
--log.level=info \
|
||||
--log.format=json
|
||||
''
|
||||
];
|
||||
};
|
||||
|
||||
"${containerName "batcher"}" = {
|
||||
image = cfg.images.batcher;
|
||||
autoStart = true;
|
||||
volumes = [ "${cfg.rootDir}/batcher:/workspace" ];
|
||||
environmentFiles = [ "${cfg.rootDir}/batcher/.env" ];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
exec op-batcher \
|
||||
--l1-eth-rpc="$L1_RPC_URL" \
|
||||
--l2-eth-rpc="$L2_RPC_URL" \
|
||||
--rollup-rpc="$ROLLUP_RPC_URL" \
|
||||
--private-key="$PRIVATE_KEY" \
|
||||
--batch-inbox-address="$BATCH_INBOX_ADDRESS" \
|
||||
--rpc.addr=127.0.0.1 \
|
||||
--rpc.port=8548 \
|
||||
--rpc.enable-admin \
|
||||
--max-channel-duration=1 \
|
||||
--data-availability-type=calldata \
|
||||
--resubmission-timeout=30s \
|
||||
--log.level=info \
|
||||
--log.format=json
|
||||
''
|
||||
];
|
||||
};
|
||||
|
||||
"${containerName "proposer"}" = {
|
||||
image = cfg.images.proposer;
|
||||
autoStart = true;
|
||||
volumes = [ "${cfg.rootDir}/proposer:/workspace" ];
|
||||
environmentFiles = [ "${cfg.rootDir}/proposer/.env" ];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
exec op-proposer \
|
||||
--rpc.port=8560 \
|
||||
--rollup-rpc="$ROLLUP_RPC_URL" \
|
||||
--l1-eth-rpc="$L1_RPC_URL" \
|
||||
--private-key="$PRIVATE_KEY" \
|
||||
--game-factory-address="$GAME_FACTORY_ADDRESS" \
|
||||
--proposal-interval="$PROPOSAL_INTERVAL" \
|
||||
--allow-non-finalized=true \
|
||||
--wait-node-sync=true \
|
||||
--log.level=info \
|
||||
--log.format=json
|
||||
''
|
||||
];
|
||||
};
|
||||
} // lib.optionalAttrs cfg.challengerEnable {
|
||||
"${containerName "challenger"}" = {
|
||||
image = cfg.images.challenger;
|
||||
autoStart = true;
|
||||
volumes = [ "${cfg.rootDir}/challenger:/workspace" ];
|
||||
environmentFiles = [ "${cfg.rootDir}/challenger/.env" ];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
exec op-challenger run-trace \
|
||||
--trace-type=cannon \
|
||||
--l1-eth-rpc="$L1_RPC_URL" \
|
||||
--l1-beacon="$L1_BEACON_URL" \
|
||||
--private-key="$PRIVATE_KEY" \
|
||||
--game-factory-address="$GAME_FACTORY_ADDRESS" \
|
||||
--cannon-l2-genesis=/workspace/genesis.json \
|
||||
--cannon-rollup-config=/workspace/rollup.json \
|
||||
--cannon-prestate="$CANNON_PRESTATE" \
|
||||
--l2-eth-rpc="$L2_RPC_URL" \
|
||||
--rollup-rpc="$ROLLUP_RPC_URL" \
|
||||
--datadir=/workspace/data \
|
||||
--log.level=info \
|
||||
--log.format=json
|
||||
''
|
||||
];
|
||||
};
|
||||
} // lib.optionalAttrs cfg.disputeMonEnable {
|
||||
"${containerName "dispute-mon"}" = {
|
||||
image = cfg.images.disputeMon;
|
||||
autoStart = true;
|
||||
volumes = [ "${cfg.rootDir}/dispute-mon:/workspace" ];
|
||||
environmentFiles = [ "${cfg.rootDir}/dispute-mon/.env" ];
|
||||
extraOptions = [ "--network=host" ];
|
||||
entrypoint = "/bin/sh";
|
||||
cmd = [
|
||||
"-lc"
|
||||
''
|
||||
exec op-dispute-mon
|
||||
''
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services = {
|
||||
every-channel-op-stack-bootstrap = {
|
||||
description = "every.channel OP Stack bootstrap";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = with pkgs; [
|
||||
bash
|
||||
coreutils
|
||||
curl
|
||||
gnutar
|
||||
gzip
|
||||
jq
|
||||
openssl
|
||||
foundry
|
||||
python3
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
export EVERY_CHANNEL_OP_STACK_ROOT=${lib.escapeShellArg cfg.rootDir}
|
||||
export EVERY_CHANNEL_OP_STACK_PRIVATE_KEY_FILE=${lib.escapeShellArg cfg.privateKeyFile}
|
||||
export EVERY_CHANNEL_OP_STACK_L1_RPC_URL=${lib.escapeShellArg cfg.l1RpcUrl}
|
||||
export EVERY_CHANNEL_OP_STACK_L1_BEACON_URL=${lib.escapeShellArg cfg.l1BeaconUrl}
|
||||
export EVERY_CHANNEL_OP_STACK_CHAIN_ID=${toString cfg.chainId}
|
||||
export EVERY_CHANNEL_OP_STACK_P2P_ADVERTISE_IP=${lib.escapeShellArg cfg.p2pAdvertiseIp}
|
||||
export EVERY_CHANNEL_OP_DEPLOYER_BIN=${lib.escapeShellArg "${cfg.rootDir}/bin/op-deployer"}
|
||||
export EVERY_CHANNEL_OP_DEPLOYER_TAG=${lib.escapeShellArg cfg.opDeployerTag}
|
||||
export EVERY_CHANNEL_OP_DEPLOYER_DOWNLOAD_SCRIPT=${lib.escapeShellArg downloadScript}
|
||||
export EVERY_CHANNEL_OP_STACK_CHALLENGER_PRESTATE_FILE=${lib.escapeShellArg (if cfg.challengerPrestateFile == null then "" else cfg.challengerPrestateFile)}
|
||||
${lib.escapeShellArg bootstrapScript}
|
||||
'';
|
||||
};
|
||||
"podman-${containerName "geth"}" = {
|
||||
after = [ "every-channel-op-stack-bootstrap.service" ];
|
||||
wants = [ "every-channel-op-stack-bootstrap.service" ];
|
||||
requires = [ "every-channel-op-stack-bootstrap.service" ];
|
||||
};
|
||||
"podman-${containerName "node"}" = {
|
||||
after = [ "every-channel-op-stack-bootstrap.service" "podman-${containerName "geth"}.service" ];
|
||||
wants = [ "every-channel-op-stack-bootstrap.service" "podman-${containerName "geth"}.service" ];
|
||||
requires = [ "every-channel-op-stack-bootstrap.service" ];
|
||||
};
|
||||
"podman-${containerName "batcher"}" = {
|
||||
after = [ "podman-${containerName "node"}.service" ];
|
||||
wants = [ "podman-${containerName "node"}.service" ];
|
||||
};
|
||||
"podman-${containerName "proposer"}" = {
|
||||
after = [ "podman-${containerName "node"}.service" ];
|
||||
wants = [ "podman-${containerName "node"}.service" ];
|
||||
};
|
||||
} // lib.optionalAttrs cfg.challengerEnable {
|
||||
"podman-${containerName "challenger"}" = {
|
||||
after = [ "podman-${containerName "node"}.service" ];
|
||||
wants = [ "podman-${containerName "node"}.service" ];
|
||||
};
|
||||
} // lib.optionalAttrs cfg.disputeMonEnable {
|
||||
"podman-${containerName "dispute-mon"}" = {
|
||||
after = [ "podman-${containerName "node"}.service" ];
|
||||
wants = [ "podman-${containerName "node"}.service" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
23
nix/modules/ec-publisher-guest.nix
Normal file
23
nix/modules/ec-publisher-guest.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{ lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
networking.hostName = lib.mkForce "ec-publisher";
|
||||
|
||||
services.every-channel.ec-node = {
|
||||
relayUrl = lib.mkDefault "https://cdn.moq.dev/anon";
|
||||
passthrough = lib.mkDefault false;
|
||||
|
||||
hdhomerun.autoDiscover = lib.mkDefault true;
|
||||
|
||||
control = {
|
||||
enable = lib.mkDefault true;
|
||||
discovery = lib.mkDefault "dht,mdns,dns";
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
ffmpeg
|
||||
jq
|
||||
];
|
||||
}
|
||||
30
nix/nixos/ecp-forge-hardware.nix
Normal file
30
nix/nixos/ecp-forge-hardware.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{ lib, ... }:
|
||||
|
||||
{
|
||||
boot.initrd.availableKernelModules = [
|
||||
"ahci"
|
||||
"xhci_pci"
|
||||
"nvme"
|
||||
"sd_mod"
|
||||
"sr_mod"
|
||||
];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-label/nixos";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-label/boot";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0077" "dmask=0077" ];
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
}
|
||||
288
nix/nixos/ecp-forge.nix
Normal file
288
nix/nixos/ecp-forge.nix
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
hasForgejoApiToken = builtins.pathExists ../../secrets/forgejo-api-token.age;
|
||||
hasNetbootChainToken = builtins.pathExists ../../secrets/netboot-chain-token.age;
|
||||
hasOpStackSepoliaKey = builtins.pathExists ../../secrets/op-stack-sepolia-private-key.age;
|
||||
hasOpStackChallengerPrestate = builtins.pathExists ../../secrets/op-stack-challenger-prestate.bin.gz.age;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./ecp-forge-hardware.nix
|
||||
];
|
||||
|
||||
networking = {
|
||||
hostName = "ecp-forge";
|
||||
hostId = "007f0200";
|
||||
useDHCP = lib.mkForce true;
|
||||
networkmanager.enable = lib.mkForce false;
|
||||
nameservers = [ "1.1.1.1" "8.8.8.8" ];
|
||||
firewall = {
|
||||
trustedInterfaces = [ "tailscale0" ];
|
||||
allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
2222
|
||||
69
|
||||
];
|
||||
allowedUDPPorts = [
|
||||
67
|
||||
69
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
startWhenNeeded = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
PermitRootLogin = lib.mkForce "prohibit-password";
|
||||
KbdInteractiveAuthentication = false;
|
||||
};
|
||||
};
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBueQxNbP2246pxr/m7au4zNVm+ShC96xuOcfEcpIjWZ"
|
||||
];
|
||||
|
||||
security.sudo = {
|
||||
execWheelOnly = true;
|
||||
wheelNeedsPassword = false;
|
||||
};
|
||||
|
||||
users = {
|
||||
mutableUsers = false;
|
||||
defaultUserShell = pkgs.bash;
|
||||
users.conradev = {
|
||||
uid = 1000;
|
||||
isNormalUser = true;
|
||||
password = "password";
|
||||
group = "conradev";
|
||||
extraGroups = [ "wheel" "docker" "libvirtd" "kvm" ];
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBueQxNbP2246pxr/m7au4zNVm+ShC96xuOcfEcpIjWZ"
|
||||
];
|
||||
};
|
||||
groups.conradev = { };
|
||||
};
|
||||
|
||||
boot.loader = {
|
||||
grub = {
|
||||
enable = true;
|
||||
device = "/dev/disk/by-id/nvme-KIOXIA_KCD81RUG7T68_25R0A0KZTTEJ";
|
||||
};
|
||||
efi.canTouchEfiVariables = false;
|
||||
};
|
||||
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
boot.zfs.extraPools = [ "tank" ];
|
||||
boot.kernel.sysctl = {
|
||||
"fs.inotify.max_user_watches" = "204800";
|
||||
};
|
||||
|
||||
hardware.nvidia-container-toolkit.enable = false;
|
||||
|
||||
virtualisation = {
|
||||
containers.enable = true;
|
||||
oci-containers.backend = "podman";
|
||||
podman = {
|
||||
enable = true;
|
||||
dockerCompat = true;
|
||||
autoPrune.enable = true;
|
||||
defaultNetwork.settings.dns_enabled = true;
|
||||
};
|
||||
};
|
||||
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
extraUpFlags = "--accept-routes";
|
||||
};
|
||||
|
||||
age.identityPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
age.secrets = lib.mkMerge [
|
||||
(lib.mkIf hasForgejoApiToken {
|
||||
"forgejo-api-token" = {
|
||||
file = ../../secrets/forgejo-api-token.age;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
})
|
||||
(lib.mkIf hasNetbootChainToken {
|
||||
"every-channel-netboot-chain-token" = {
|
||||
file = ../../secrets/netboot-chain-token.age;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
})
|
||||
(lib.mkIf hasOpStackSepoliaKey {
|
||||
"every-channel-op-stack-sepolia-private-key" = {
|
||||
file = ../../secrets/op-stack-sepolia-private-key.age;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
})
|
||||
(lib.mkIf hasOpStackChallengerPrestate {
|
||||
"every-channel-op-stack-challenger-prestate" = {
|
||||
file = ../../secrets/op-stack-challenger-prestate.bin.gz.age;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
services.zfs.autoScrub = {
|
||||
enable = true;
|
||||
pools = [ "tank" ];
|
||||
interval = "weekly";
|
||||
};
|
||||
|
||||
services.nfs.server = {
|
||||
enable = true;
|
||||
# Keep NFS on trusted/private paths only; public Hetzner exposure triggers
|
||||
# rpcbind/portmapper enumeration and is not needed for forge access.
|
||||
exports = ''
|
||||
/tank 10.0.0.0/8(rw,fsid=0,crossmnt,no_subtree_check,sync) 100.64.0.0/10(rw,fsid=0,crossmnt,no_subtree_check,sync) 192.168.0.0/16(rw,fsid=0,crossmnt,no_subtree_check,sync)
|
||||
'';
|
||||
};
|
||||
|
||||
nix.gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
|
||||
nix.settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
substituters = lib.mkForce [ "https://cache.nixos.org" ];
|
||||
trusted-substituters = lib.mkForce [ "https://cache.nixos.org" ];
|
||||
extra-substituters = lib.mkForce [ ];
|
||||
trusted-public-keys = lib.mkForce [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
];
|
||||
extra-trusted-public-keys = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
time.timeZone = "America/New_York";
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
xdg.mime.enable = true;
|
||||
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
database.type = "sqlite3";
|
||||
lfs.enable = true;
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "git.every.channel";
|
||||
ROOT_URL = "https://git.every.channel/";
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
HTTP_PORT = 3000;
|
||||
SSH_DOMAIN = "git.every.channel";
|
||||
SSH_PORT = 2222;
|
||||
SSH_LISTEN_PORT = 2222;
|
||||
START_SSH_SERVER = true;
|
||||
};
|
||||
service = {
|
||||
DISABLE_REGISTRATION = true;
|
||||
REQUIRE_SIGNIN_VIEW = false;
|
||||
};
|
||||
repository = {
|
||||
DEFAULT_BRANCH = "main";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
email = "infra@every.channel";
|
||||
acmeCA = "https://acme-v02.api.letsencrypt.org/directory";
|
||||
virtualHosts = {
|
||||
"git.every.channel".extraConfig = ''
|
||||
reverse_proxy http://127.0.0.1:3000
|
||||
'';
|
||||
"forge.every.channel".extraConfig = ''
|
||||
redir https://git.every.channel{uri} permanent
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
services.every-channel.netboot = {
|
||||
enable = true;
|
||||
listenIP = "95.216.114.54";
|
||||
interface = "enp5s0f3u2u2c2";
|
||||
hostname = "boot.every.channel";
|
||||
httpPort = 8080;
|
||||
tftpBootFilename = "ec-ipxe.efi";
|
||||
httpAllowedCIDRs = [
|
||||
"10.0.0.0/8"
|
||||
"172.16.0.0/12"
|
||||
"192.168.0.0/16"
|
||||
"100.64.0.0/10"
|
||||
];
|
||||
chainTokenFile =
|
||||
if hasNetbootChainToken
|
||||
then config.age.secrets."every-channel-netboot-chain-token".path
|
||||
else null;
|
||||
proxyDhcp.enable = false;
|
||||
release.host = "https://git.every.channel";
|
||||
release.repo = "every-channel/every.channel";
|
||||
release.localTarball = "/var/lib/every-channel-netboot/sources/ec-runner-x86_64-netboot-local.tar.gz";
|
||||
release.tokenFile =
|
||||
if hasForgejoApiToken
|
||||
then config.age.secrets."forgejo-api-token".path
|
||||
else null;
|
||||
};
|
||||
|
||||
services.every-channel.ipxe-qemu = {
|
||||
enable = true;
|
||||
name = "ecp-forge-ipxe";
|
||||
boot.userNet.hostname = "ecp-forge-ipxe";
|
||||
boot.http.root = "${config.services.every-channel.netboot.rootDir}/http";
|
||||
};
|
||||
|
||||
systemd.services.every-channel-ipxe-qemu = {
|
||||
after = [ "every-channel-netboot-stage.service" ];
|
||||
wants = [ "every-channel-netboot-stage.service" ];
|
||||
};
|
||||
|
||||
services.every-channel.ec-node = {
|
||||
enable = true;
|
||||
archive = {
|
||||
enable = true;
|
||||
outputDir = "/tank/every-channel/archive";
|
||||
manifestDir = "/var/lib/every-channel/manifests";
|
||||
# Keep forge archival as an ingest worker only; replay serving is separate.
|
||||
serve.enable = false;
|
||||
};
|
||||
};
|
||||
|
||||
services.every-channel.op-stack = {
|
||||
enable = hasOpStackSepoliaKey;
|
||||
challengerEnable = hasOpStackChallengerPrestate;
|
||||
disputeMonEnable = hasOpStackChallengerPrestate;
|
||||
privateKeyFile =
|
||||
if hasOpStackSepoliaKey
|
||||
then config.age.secrets."every-channel-op-stack-sepolia-private-key".path
|
||||
else null;
|
||||
challengerPrestateFile =
|
||||
if hasOpStackChallengerPrestate
|
||||
then config.age.secrets."every-channel-op-stack-challenger-prestate".path
|
||||
else null;
|
||||
p2pAdvertiseIp = "95.216.114.54";
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
git
|
||||
htop
|
||||
jq
|
||||
tmux
|
||||
zfs
|
||||
];
|
||||
|
||||
system.stateVersion = "22.11";
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|||
cd "${root}"
|
||||
|
||||
in_file="${1:-secrets/token.txt}"
|
||||
out_file="${2:-secrets/forge-token.age}"
|
||||
out_file="${2:-secrets/forgejo-api-token.age}"
|
||||
|
||||
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-${root}/secrets.nix}"
|
||||
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||
|
|
|
|||
23
scripts/deploy-ecp-forge.sh
Executable file
23
scripts/deploy-ecp-forge.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
target="${EVERY_CHANNEL_FORGE_TARGET_HOST:-root@git.every.channel}"
|
||||
build_host="${EVERY_CHANNEL_FORGE_BUILD_HOST:-${target}}"
|
||||
identity_file="${EVERY_CHANNEL_FORGE_SSH_IDENTITY:-$HOME/.ssh/id_ed25519}"
|
||||
|
||||
if [[ ! -f "${identity_file}" ]]; then
|
||||
echo "error: SSH identity not found: ${identity_file}" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
export NIX_SSHOPTS="-o BatchMode=yes -o IdentityAgent=none -o IdentitiesOnly=yes -i ${identity_file}"
|
||||
|
||||
exec nix run nixpkgs#nixos-rebuild -- \
|
||||
--flake "${root}#ecp-forge" \
|
||||
--target-host "${target}" \
|
||||
--build-host "${build_host}" \
|
||||
--use-remote-sudo \
|
||||
switch
|
||||
|
|
@ -8,12 +8,13 @@ cd "${root}"
|
|||
#
|
||||
# Auth token source order:
|
||||
# 1) EVERY_CHANNEL_FORGE_TOKEN / FORGE_TOKEN / CODEBERG_TOKEN env var
|
||||
# 2) `agenix -d secrets/forge-token.age` or `secrets/codeberg-token.age` (optional)
|
||||
# 3) `age -d -i <identity> secrets/forge-token.age` or `secrets/codeberg-token.age` (optional)
|
||||
# 2) `agenix -d secrets/forgejo-api-token.age` (preferred) / `secrets/forge-token.age` / `secrets/codeberg-token.age` (optional)
|
||||
# 3) `age -d -i <identity> secrets/forgejo-api-token.age` / `secrets/forge-token.age` / `secrets/codeberg-token.age` (optional)
|
||||
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://git.every.channel}"
|
||||
account="${EVERY_CHANNEL_FORGE_ACCOUNT:-every-channel}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forgejo-api-token.age}"
|
||||
token_file_secondary="${EVERY_CHANNEL_LEGACY_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
|
||||
|
||||
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
|
||||
|
|
@ -38,6 +39,9 @@ load_token_from_file() {
|
|||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_primary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_secondary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_compat}" || true)"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
|||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://git.every.channel}"
|
||||
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||
branch="${EVERY_CHANNEL_PROTECTED_BRANCH:-main}"
|
||||
required_checks_csv="${EVERY_CHANNEL_REQUIRED_CHECKS:-ci-gates / checks}"
|
||||
|
|
@ -13,7 +13,8 @@ require_signed_commits_raw="${EVERY_CHANNEL_REQUIRE_SIGNED_COMMITS:-true}"
|
|||
|
||||
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
|
||||
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forgejo-api-token.age}"
|
||||
token_file_secondary="${EVERY_CHANNEL_LEGACY_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
|
||||
|
||||
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
|
||||
|
|
@ -35,6 +36,9 @@ load_token_from_file() {
|
|||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_primary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_secondary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_compat}" || true)"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
|||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://git.every.channel}"
|
||||
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||
secret_name="${EVERY_CHANNEL_FORGE_AGE_SECRET_NAME:-AGE_FORGE_SSH_KEY}"
|
||||
key_path="${1:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ set -euo pipefail
|
|||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
||||
host="${EVERY_CHANNEL_FORGE_HOST:-https://git.every.channel}"
|
||||
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||
enabled_raw="${EVERY_CHANNEL_FORGE_ACTIONS_ENABLED:-false}"
|
||||
|
||||
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
|
||||
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forgejo-api-token.age}"
|
||||
token_file_secondary="${EVERY_CHANNEL_LEGACY_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
|
||||
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
|
||||
|
||||
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
|
||||
|
|
@ -32,6 +33,9 @@ load_token_from_file() {
|
|||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_primary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_secondary}" || true)"
|
||||
fi
|
||||
if [[ -z "${token}" ]]; then
|
||||
token="$(load_token_from_file "${token_file_compat}" || true)"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|||
cd "${root}"
|
||||
|
||||
primary_remote="${EVERY_CHANNEL_PRIMARY_REMOTE:-origin}"
|
||||
primary_url="${EVERY_CHANNEL_PRIMARY_GIT_URL:-git@forge.every.channel:every-channel/every.channel.git}"
|
||||
primary_url="${EVERY_CHANNEL_PRIMARY_GIT_URL:-ssh://forgejo@git.every.channel:2222/every-channel/every.channel.git}"
|
||||
|
||||
codeberg_remote="${EVERY_CHANNEL_CODEBERG_REMOTE:-mirror-codeberg}"
|
||||
codeberg_url="${EVERY_CHANNEL_CODEBERG_GIT_URL:-git@codeberg.org:every-channel/every.channel.git}"
|
||||
|
|
|
|||
147
scripts/netboot-build-ipxe.sh
Executable file
147
scripts/netboot-build-ipxe.sh
Executable file
|
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
netboot_root="${EVERY_CHANNEL_NETBOOT_ROOT:-tmp/netboot}"
|
||||
tftp_dir="${netboot_root}/tftp"
|
||||
netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-boot.every.channel}"
|
||||
http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}"
|
||||
ipxe_repo="${EVERY_CHANNEL_NETBOOT_IPXE_REPO:-https://github.com/ipxe/ipxe.git}"
|
||||
ipxe_ref="${EVERY_CHANNEL_NETBOOT_IPXE_REF:-}"
|
||||
output_name="${EVERY_CHANNEL_NETBOOT_IPXE_FILENAME:-ec-ipxe.efi}"
|
||||
use_docker="${EVERY_CHANNEL_NETBOOT_IPXE_USE_DOCKER:-auto}"
|
||||
docker_image="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_IMAGE:-ubuntu:24.04}"
|
||||
docker_platform="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_PLATFORM:-linux/amd64}"
|
||||
chain_token="${EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN:-}"
|
||||
|
||||
need_cmd() {
|
||||
local name="$1"
|
||||
if ! command -v "${name}" >/dev/null 2>&1; then
|
||||
echo "error: required command not found: ${name}" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
validate_chain_token() {
|
||||
local value="$1"
|
||||
if [[ -z "${value}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ! "${value}" =~ ^[A-Za-z0-9._~-]{16,128}$ ]]; then
|
||||
echo "error: EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN must match [A-Za-z0-9._~-]{16,128}" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
validate_chain_token "${chain_token}"
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "${tmp_dir}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
repo_dir="${tmp_dir}/ipxe"
|
||||
embed_script="${tmp_dir}/embed.ipxe"
|
||||
build_output="${tmp_dir}/ipxe.efi"
|
||||
|
||||
chain_url="http://${netboot_hostname}:${http_port}/netboot.ipxe"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
chain_url="${chain_url}?token=${chain_token}"
|
||||
fi
|
||||
|
||||
cat > "${embed_script}" <<EOF
|
||||
#!ipxe
|
||||
dhcp
|
||||
chain ${chain_url} || shell
|
||||
EOF
|
||||
|
||||
bool_norm() {
|
||||
local raw
|
||||
raw="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
||||
case "${raw}" in
|
||||
''|auto) echo "auto" ;;
|
||||
true|1|yes|y|on) echo "true" ;;
|
||||
false|0|no|n|off) echo "false" ;;
|
||||
*)
|
||||
echo "error: invalid boolean value '${1}'" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
build_native() {
|
||||
need_cmd git
|
||||
need_cmd make
|
||||
if ! command -v cc >/dev/null 2>&1 && ! command -v gcc >/dev/null 2>&1; then
|
||||
echo "error: required C compiler not found (need cc or gcc)" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! command -v objcopy >/dev/null 2>&1; then
|
||||
echo "error: required tool not found: objcopy" >&2
|
||||
return 1
|
||||
fi
|
||||
git clone --depth 1 "${ipxe_repo}" "${repo_dir}" >/dev/null
|
||||
if [[ -n "${ipxe_ref}" ]]; then
|
||||
git -C "${repo_dir}" fetch --depth 1 origin "${ipxe_ref}" >/dev/null
|
||||
git -C "${repo_dir}" checkout -q FETCH_HEAD
|
||||
fi
|
||||
make -C "${repo_dir}/src" -s "bin-x86_64-efi/ipxe.efi" "EMBED=${embed_script}"
|
||||
cp -f "${repo_dir}/src/bin-x86_64-efi/ipxe.efi" "${build_output}"
|
||||
}
|
||||
|
||||
build_docker() {
|
||||
need_cmd docker
|
||||
docker run --rm \
|
||||
--platform "${docker_platform}" \
|
||||
-v "${tmp_dir}:/work" \
|
||||
-e IPXE_REPO="${ipxe_repo}" \
|
||||
-e IPXE_REF="${ipxe_ref}" \
|
||||
-w /work \
|
||||
"${docker_image}" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
apt-get update >/dev/null
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates git make gcc binutils perl mtools >/dev/null
|
||||
git clone --depth 1 "${IPXE_REPO}" ipxe >/dev/null
|
||||
if [ -n "${IPXE_REF}" ]; then
|
||||
git -C ipxe fetch --depth 1 origin "${IPXE_REF}" >/dev/null
|
||||
git -C ipxe checkout -q FETCH_HEAD
|
||||
fi
|
||||
make -C ipxe/src -s "bin-x86_64-efi/ipxe.efi" "EMBED=/work/embed.ipxe"
|
||||
'
|
||||
cp -f "${tmp_dir}/ipxe/src/bin-x86_64-efi/ipxe.efi" "${build_output}"
|
||||
}
|
||||
|
||||
use_docker="$(bool_norm "${use_docker}")"
|
||||
build_ok="false"
|
||||
if [[ "${use_docker}" != "true" ]]; then
|
||||
if build_native; then
|
||||
build_ok="true"
|
||||
fi
|
||||
fi
|
||||
if [[ "${build_ok}" != "true" ]]; then
|
||||
if [[ "${use_docker}" == "false" ]]; then
|
||||
exit 2
|
||||
fi
|
||||
build_docker
|
||||
fi
|
||||
|
||||
mkdir -p "${tftp_dir}"
|
||||
cp -f "${build_output}" "${tftp_dir}/${output_name}"
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256"
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256"
|
||||
fi
|
||||
|
||||
echo "ok: built embedded iPXE binary: ${tftp_dir}/${output_name}"
|
||||
echo "ok: chains to ${chain_url}"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
echo "ok: chain token enabled"
|
||||
fi
|
||||
echo "hint: in UniFi set DHCP boot filename to ${output_name}"
|
||||
144
scripts/netboot-http-server.py
Executable file
144
scripts/netboot-http-server.py
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Minimal HTTP server for netboot artifacts with optional CIDR/token controls."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import ipaddress
|
||||
import pathlib
|
||||
import urllib.parse
|
||||
from http import HTTPStatus
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from typing import Union
|
||||
|
||||
|
||||
ALLOWED_FILES = {
|
||||
"kernel": "application/octet-stream",
|
||||
"bzImage": "application/octet-stream",
|
||||
"initrd": "application/octet-stream",
|
||||
"netboot.ipxe": "text/plain; charset=utf-8",
|
||||
}
|
||||
|
||||
Network = Union[ipaddress.IPv4Network, ipaddress.IPv6Network]
|
||||
|
||||
|
||||
class NetbootHTTPServer(ThreadingHTTPServer):
|
||||
def __init__(
|
||||
self,
|
||||
server_address: tuple[str, int],
|
||||
root: pathlib.Path,
|
||||
allowed_networks: list[Network],
|
||||
netboot_token: str | None,
|
||||
) -> None:
|
||||
super().__init__(server_address, NetbootRequestHandler)
|
||||
self.root = root
|
||||
self.allowed_networks = allowed_networks
|
||||
self.netboot_token = netboot_token
|
||||
|
||||
|
||||
class NetbootRequestHandler(BaseHTTPRequestHandler):
|
||||
server_version = "ec-netboot/1.0"
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
def do_GET(self) -> None:
|
||||
self._serve_file(include_body=True)
|
||||
|
||||
def do_HEAD(self) -> None:
|
||||
self._serve_file(include_body=False)
|
||||
|
||||
def _serve_file(self, include_body: bool) -> None:
|
||||
if not self._client_allowed():
|
||||
self._send_error(HTTPStatus.FORBIDDEN, "client not allowed")
|
||||
return
|
||||
|
||||
parsed = urllib.parse.urlparse(self.path)
|
||||
path = parsed.path.lstrip("/")
|
||||
if path not in ALLOWED_FILES:
|
||||
self._send_error(HTTPStatus.NOT_FOUND, "file not found")
|
||||
return
|
||||
|
||||
if path == "netboot.ipxe" and self.server.netboot_token:
|
||||
query = urllib.parse.parse_qs(parsed.query, keep_blank_values=True)
|
||||
token = query.get("token", [""])[0]
|
||||
if token != self.server.netboot_token:
|
||||
self._send_error(HTTPStatus.FORBIDDEN, "missing or invalid token")
|
||||
return
|
||||
|
||||
if path == "bzImage":
|
||||
file_path = self.server.root / "kernel"
|
||||
else:
|
||||
file_path = self.server.root / path
|
||||
if not file_path.is_file():
|
||||
self._send_error(HTTPStatus.NOT_FOUND, "file not found")
|
||||
return
|
||||
|
||||
stat = file_path.stat()
|
||||
self.send_response(HTTPStatus.OK)
|
||||
self.send_header("Content-Type", ALLOWED_FILES[path])
|
||||
self.send_header("Content-Length", str(stat.st_size))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.end_headers()
|
||||
|
||||
if not include_body:
|
||||
return
|
||||
|
||||
with file_path.open("rb") as handle:
|
||||
while True:
|
||||
chunk = handle.read(64 * 1024)
|
||||
if not chunk:
|
||||
break
|
||||
self.wfile.write(chunk)
|
||||
|
||||
def _client_allowed(self) -> bool:
|
||||
networks = self.server.allowed_networks
|
||||
if not networks:
|
||||
return True
|
||||
|
||||
try:
|
||||
client_ip = ipaddress.ip_address(self.client_address[0])
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return any(client_ip in network for network in networks)
|
||||
|
||||
def _send_error(self, code: HTTPStatus, message: str) -> None:
|
||||
payload = (message + "\n").encode("utf-8")
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.send_header("Content-Length", str(len(payload)))
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.end_headers()
|
||||
self.wfile.write(payload)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--bind-ip", required=True)
|
||||
parser.add_argument("--port", type=int, required=True)
|
||||
parser.add_argument("--root", required=True)
|
||||
parser.add_argument("--allow-cidr", action="append", default=[])
|
||||
parser.add_argument("--netboot-token", default="")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
root = pathlib.Path(args.root).resolve()
|
||||
if not root.is_dir():
|
||||
raise SystemExit(f"error: root directory does not exist: {root}")
|
||||
|
||||
allowed_networks = []
|
||||
for cidr in args.allow_cidr:
|
||||
try:
|
||||
allowed_networks.append(ipaddress.ip_network(cidr, strict=False))
|
||||
except ValueError as exc:
|
||||
raise SystemExit(f"error: invalid CIDR '{cidr}': {exc}") from exc
|
||||
|
||||
token = args.netboot_token or None
|
||||
server = NetbootHTTPServer((args.bind_ip, args.port), root, allowed_networks, token)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -14,6 +14,10 @@ proxy_subnet="${EVERY_CHANNEL_NETBOOT_PROXY_SUBNET:-}"
|
|||
netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-}"
|
||||
http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}"
|
||||
dnsmasq_port="${EVERY_CHANNEL_NETBOOT_DNS_PORT:-0}"
|
||||
proxy_dhcp="${EVERY_CHANNEL_NETBOOT_PROXY_DHCP:-true}"
|
||||
tftp_boot_filename="${EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME:-ipxe.efi}"
|
||||
http_allowed_cidrs="${EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS:-}"
|
||||
chain_token="${EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN:-}"
|
||||
|
||||
need_cmd() {
|
||||
local name="$1"
|
||||
|
|
@ -26,10 +30,43 @@ need_cmd() {
|
|||
need_cmd dnsmasq
|
||||
need_cmd python3
|
||||
|
||||
bool_norm() {
|
||||
local raw
|
||||
raw="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
||||
case "${raw}" in
|
||||
''|true|1|yes|y|on) echo "true" ;;
|
||||
false|0|no|n|off) echo "false" ;;
|
||||
*)
|
||||
echo "error: invalid boolean value '${1}'" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
trim_ws() {
|
||||
local value="$1"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
value="${value%"${value##*[![:space:]]}"}"
|
||||
printf '%s' "${value}"
|
||||
}
|
||||
|
||||
validate_chain_token() {
|
||||
local value="$1"
|
||||
if [[ -z "${value}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ! "${value}" =~ ^[A-Za-z0-9._~-]{16,128}$ ]]; then
|
||||
echo "error: EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN must match [A-Za-z0-9._~-]{16,128}" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "error: netboot-serve requires root (TFTP + ProxyDHCP ports)." >&2
|
||||
echo "hint: run with sudo and pass env vars, for example:" >&2
|
||||
echo " sudo EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 EVERY_CHANNEL_NETBOOT_PROXY_SUBNET=10.20.30.0/24 EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel ./scripts/netboot-serve.sh" >&2
|
||||
echo "hint: run with sudo and pass env vars. Example (UniFi-only):" >&2
|
||||
echo " sudo EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel EVERY_CHANNEL_NETBOOT_PROXY_DHCP=false EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME=ec-ipxe.efi ./scripts/netboot-serve.sh" >&2
|
||||
echo "hint: Example (ProxyDHCP):" >&2
|
||||
echo " sudo EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 EVERY_CHANNEL_NETBOOT_PROXY_SUBNET=10.20.30.0/24 EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel EVERY_CHANNEL_NETBOOT_PROXY_DHCP=true ./scripts/netboot-serve.sh" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
|
@ -41,15 +78,25 @@ if [[ -z "${interface_name}" ]]; then
|
|||
echo "error: set EVERY_CHANNEL_NETBOOT_INTERFACE (interface on NUC VLAN)" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ -z "${proxy_subnet}" ]]; then
|
||||
echo "error: set EVERY_CHANNEL_NETBOOT_PROXY_SUBNET (for example 10.20.30.0/24)" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ -z "${netboot_hostname}" ]]; then
|
||||
netboot_hostname="${listen_ip}"
|
||||
fi
|
||||
proxy_dhcp="$(bool_norm "${proxy_dhcp}")"
|
||||
validate_chain_token "${chain_token}"
|
||||
if [[ "${proxy_dhcp}" == "true" && -z "${proxy_subnet}" ]]; then
|
||||
echo "error: set EVERY_CHANNEL_NETBOOT_PROXY_SUBNET (for example 10.20.30.0/24) when proxy mode is enabled" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ -z "${http_allowed_cidrs}" && "${proxy_dhcp}" == "true" ]]; then
|
||||
http_allowed_cidrs="${proxy_subnet}"
|
||||
fi
|
||||
|
||||
for required in "${http_dir}/kernel" "${http_dir}/initrd" "${http_dir}/netboot.ipxe" "${tftp_dir}/ipxe.efi"; do
|
||||
netboot_chain_url="http://${netboot_hostname}:${http_port}/netboot.ipxe"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
netboot_chain_url="${netboot_chain_url}?token=${chain_token}"
|
||||
fi
|
||||
|
||||
for required in "${http_dir}/kernel" "${http_dir}/initrd" "${http_dir}/netboot.ipxe" "${tftp_dir}/${tftp_boot_filename}"; do
|
||||
if [[ ! -f "${required}" ]]; then
|
||||
echo "error: missing required staged file: ${required}" >&2
|
||||
echo "hint: run ./scripts/netboot-stage.sh first" >&2
|
||||
|
|
@ -75,25 +122,73 @@ listen-address=${listen_ip}
|
|||
log-dhcp
|
||||
enable-tftp
|
||||
tftp-root=${tftp_dir}
|
||||
EOF
|
||||
|
||||
if [[ "${proxy_dhcp}" == "true" ]]; then
|
||||
cat >> "${run_dir}/dnsmasq.conf" <<EOF
|
||||
dhcp-range=${proxy_subnet},proxy
|
||||
dhcp-userclass=set:ipxe,iPXE
|
||||
dhcp-match=set:efi64,option:client-arch,7
|
||||
dhcp-match=set:efi64,option:client-arch,9
|
||||
dhcp-option=66,${netboot_hostname}
|
||||
dhcp-boot=tag:!ipxe,tag:efi64,ipxe.efi
|
||||
dhcp-boot=tag:ipxe,tag:efi64,http://${netboot_hostname}:${http_port}/netboot.ipxe
|
||||
dhcp-boot=tag:!ipxe,ipxe.efi
|
||||
dhcp-boot=tag:ipxe,http://${netboot_hostname}:${http_port}/netboot.ipxe
|
||||
dhcp-boot=tag:!ipxe,tag:efi64,${tftp_boot_filename}
|
||||
dhcp-boot=tag:ipxe,tag:efi64,${netboot_chain_url}
|
||||
dhcp-boot=tag:!ipxe,${tftp_boot_filename}
|
||||
dhcp-boot=tag:ipxe,${netboot_chain_url}
|
||||
EOF
|
||||
fi
|
||||
|
||||
python3 -m http.server "${http_port}" --bind "${listen_ip}" --directory "${http_dir}" >/tmp/every-channel-netboot-http.log 2>&1 &
|
||||
http_server_script="${root}/scripts/netboot-http-server.py"
|
||||
if [[ ! -f "${http_server_script}" ]]; then
|
||||
echo "error: missing HTTP server helper: ${http_server_script}" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
http_args=(python3 "${http_server_script}" --bind-ip "${listen_ip}" --port "${http_port}" --root "${http_dir}")
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
http_args+=(--netboot-token "${chain_token}")
|
||||
fi
|
||||
|
||||
if [[ -n "${http_allowed_cidrs}" ]]; then
|
||||
IFS=',' read -r -a cidr_raw <<< "${http_allowed_cidrs}"
|
||||
for raw in "${cidr_raw[@]}"; do
|
||||
cidr="$(trim_ws "${raw}")"
|
||||
if [[ -n "${cidr}" ]]; then
|
||||
http_args+=(--allow-cidr "${cidr}")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
http_log="${run_dir}/http.log"
|
||||
"${http_args[@]}" >"${http_log}" 2>&1 &
|
||||
http_pid="$!"
|
||||
sleep 0.2
|
||||
if ! kill -0 "${http_pid}" >/dev/null 2>&1; then
|
||||
echo "error: HTTP server failed to start; see ${http_log}" >&2
|
||||
cat "${http_log}" >&2 || true
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "ok: HTTP serving ${http_dir} on http://${listen_ip}:${http_port}/"
|
||||
echo "ok: advertised netboot host: ${netboot_hostname}"
|
||||
echo "ok: TFTP serving ${tftp_dir} on ${listen_ip}:69"
|
||||
echo "ok: TFTP boot filename: ${tftp_boot_filename}"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
echo "ok: chain token enabled"
|
||||
fi
|
||||
if [[ -n "${http_allowed_cidrs}" ]]; then
|
||||
echo "ok: HTTP allowed CIDRs: ${http_allowed_cidrs}"
|
||||
else
|
||||
echo "warning: HTTP CIDR allowlist is disabled; set EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS to lock this down"
|
||||
fi
|
||||
if [[ "${proxy_dhcp}" == "true" ]]; then
|
||||
echo "ok: ProxyDHCP active for ${proxy_subnet} on interface ${interface_name}"
|
||||
echo "ok: Use normal Unifi DHCP for IP assignment; do not configure Unifi DHCP bootfile while proxy mode is active."
|
||||
else
|
||||
echo "ok: ProxyDHCP disabled."
|
||||
echo "ok: Configure UniFi DHCP option 66=${netboot_hostname}, option 67=${tftp_boot_filename}"
|
||||
fi
|
||||
echo "ok: chain URL: ${netboot_chain_url}"
|
||||
echo
|
||||
echo "Press Ctrl+C to stop."
|
||||
dnsmasq --no-daemon --conf-file="${run_dir}/dnsmasq.conf"
|
||||
|
|
|
|||
|
|
@ -4,14 +4,20 @@ set -euo pipefail
|
|||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${root}"
|
||||
|
||||
forge_host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
||||
forge_host="${EVERY_CHANNEL_FORGE_HOST:-https://git.every.channel}"
|
||||
forge_repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||
release_tag="${EVERY_CHANNEL_NETBOOT_RELEASE_TAG:-}"
|
||||
local_tarball="${EVERY_CHANNEL_NETBOOT_TARBALL:-}"
|
||||
out_root="${EVERY_CHANNEL_NETBOOT_ROOT:-tmp/netboot}"
|
||||
ipxe_efi_url="${EVERY_CHANNEL_IPXE_EFI_URL:-https://boot.ipxe.org/snponly.efi}"
|
||||
ipxe_efi_path="${EVERY_CHANNEL_IPXE_EFI_PATH:-}"
|
||||
ipxe_efi_filename="${EVERY_CHANNEL_IPXE_EFI_FILENAME:-ipxe.efi}"
|
||||
netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-boot.every.channel}"
|
||||
http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}"
|
||||
verify_release_checksums="${EVERY_CHANNEL_NETBOOT_VERIFY_RELEASE_CHECKSUMS:-true}"
|
||||
allow_remote_ipxe="${EVERY_CHANNEL_NETBOOT_ALLOW_REMOTE_IPXE:-false}"
|
||||
ipxe_efi_sha256="${EVERY_CHANNEL_IPXE_EFI_SHA256:-}"
|
||||
chain_token="${EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN:-}"
|
||||
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
|
||||
|
||||
need_cmd() {
|
||||
|
|
@ -26,6 +32,54 @@ need_cmd curl
|
|||
need_cmd tar
|
||||
need_cmd python3
|
||||
|
||||
bool_norm() {
|
||||
local raw
|
||||
raw="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
||||
case "${raw}" in
|
||||
''|true|1|yes|y|on) echo "true" ;;
|
||||
false|0|no|n|off) echo "false" ;;
|
||||
*)
|
||||
echo "error: invalid boolean value '${1}'" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_sha256() {
|
||||
local file_path="$1"
|
||||
local expected="$2"
|
||||
local actual
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
actual="$(sha256sum "${file_path}" | awk '{print $1}')"
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
actual="$(shasum -a 256 "${file_path}" | awk '{print $1}')"
|
||||
else
|
||||
echo "error: checksum verification requested but no sha256 tool is available" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ "${actual}" != "${expected}" ]]; then
|
||||
echo "error: checksum mismatch for ${file_path}" >&2
|
||||
exit 2
|
||||
fi
|
||||
echo "$(basename "${file_path}"): OK"
|
||||
}
|
||||
|
||||
validate_chain_token() {
|
||||
local value="$1"
|
||||
if [[ -z "${value}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ! "${value}" =~ ^[A-Za-z0-9._~-]{16,128}$ ]]; then
|
||||
echo "error: EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN must match [A-Za-z0-9._~-]{16,128}" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
allow_remote_ipxe="$(bool_norm "${allow_remote_ipxe}")"
|
||||
validate_chain_token "${chain_token}"
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "${tmp_dir}"
|
||||
|
|
@ -34,6 +88,8 @@ trap cleanup EXIT
|
|||
|
||||
archive_path="${tmp_dir}/netboot.tar.gz"
|
||||
release_asset_url=""
|
||||
release_asset_name=""
|
||||
checksum_asset_url=""
|
||||
|
||||
if [[ -n "${local_tarball}" ]]; then
|
||||
if [[ ! -f "${local_tarball}" ]]; then
|
||||
|
|
@ -56,7 +112,7 @@ else
|
|||
release_json="${tmp_dir}/release.json"
|
||||
curl -fsSL "${auth_args[@]}" "${release_endpoint}" -o "${release_json}"
|
||||
|
||||
release_asset_url="$(
|
||||
parsed_assets="$(
|
||||
python3 - "${release_json}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
|
@ -77,9 +133,20 @@ if not candidates:
|
|||
|
||||
# Pick newest by release ordering if API already sorted; otherwise prefer largest id.
|
||||
chosen = sorted(candidates, key=lambda x: x.get("id", 0))[-1]
|
||||
checksum_asset = None
|
||||
for asset in assets:
|
||||
if asset.get("name") == "SHA256SUMS.txt":
|
||||
checksum_asset = asset
|
||||
break
|
||||
|
||||
print(chosen.get("name", ""))
|
||||
print(chosen.get("browser_download_url", ""))
|
||||
print("" if checksum_asset is None else checksum_asset.get("browser_download_url", ""))
|
||||
PY
|
||||
)"
|
||||
release_asset_name="$(printf '%s\n' "${parsed_assets}" | sed -n '1p')"
|
||||
release_asset_url="$(printf '%s\n' "${parsed_assets}" | sed -n '2p')"
|
||||
checksum_asset_url="$(printf '%s\n' "${parsed_assets}" | sed -n '3p')"
|
||||
|
||||
if [[ -z "${release_asset_url}" ]]; then
|
||||
echo "error: unable to find x86_64 netboot asset in release" >&2
|
||||
|
|
@ -87,6 +154,28 @@ PY
|
|||
fi
|
||||
|
||||
curl -fsSL "${auth_args[@]}" -o "${archive_path}" "${release_asset_url}"
|
||||
|
||||
verify_release_checksums_lc="$(printf '%s' "${verify_release_checksums}" | tr '[:upper:]' '[:lower:]')"
|
||||
if [[ "${verify_release_checksums_lc}" != "false" && -n "${checksum_asset_url}" ]]; then
|
||||
checksum_file="${tmp_dir}/SHA256SUMS.txt"
|
||||
curl -fsSL "${auth_args[@]}" -o "${checksum_file}" "${checksum_asset_url}"
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
(
|
||||
cd "${tmp_dir}"
|
||||
grep -F " ${release_asset_name}" "${checksum_file}" | sha256sum -c -
|
||||
)
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
expected="$(grep -F " ${release_asset_name}" "${checksum_file}" | awk '{print $1}')"
|
||||
actual="$(shasum -a 256 "${archive_path}" | awk '{print $1}')"
|
||||
if [[ -z "${expected}" || "${expected}" != "${actual}" ]]; then
|
||||
echo "error: checksum mismatch for ${release_asset_name}" >&2
|
||||
exit 2
|
||||
fi
|
||||
echo "${release_asset_name}: OK"
|
||||
else
|
||||
echo "warning: no sha256 tool available; skipping checksum verification"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
http_dir="${out_root}/http"
|
||||
|
|
@ -103,23 +192,47 @@ for required in kernel initrd netboot.ipxe; do
|
|||
fi
|
||||
done
|
||||
|
||||
curl -fsSL -o "${tftp_dir}/ipxe.efi" "${ipxe_efi_url}"
|
||||
if [[ -n "${ipxe_efi_path}" ]]; then
|
||||
if [[ ! -f "${ipxe_efi_path}" ]]; then
|
||||
echo "error: iPXE file path not found: ${ipxe_efi_path}" >&2
|
||||
exit 2
|
||||
fi
|
||||
ipxe_dest="${tftp_dir}/${ipxe_efi_filename}"
|
||||
src_resolved="$(realpath -m "${ipxe_efi_path}")"
|
||||
dst_resolved="$(realpath -m "${ipxe_dest}")"
|
||||
# When embedded iPXE already wrote to the destination path, skip self-copy.
|
||||
if [[ "${src_resolved}" != "${dst_resolved}" ]]; then
|
||||
cp -f "${ipxe_efi_path}" "${ipxe_dest}"
|
||||
fi
|
||||
else
|
||||
if [[ "${allow_remote_ipxe}" != "true" ]]; then
|
||||
echo "error: remote iPXE download is disabled by default" >&2
|
||||
echo "hint: build local iPXE with ./scripts/netboot-build-ipxe.sh and set EVERY_CHANNEL_IPXE_EFI_PATH" >&2
|
||||
echo "hint: if you must download from URL, set EVERY_CHANNEL_NETBOOT_ALLOW_REMOTE_IPXE=true" >&2
|
||||
exit 2
|
||||
fi
|
||||
curl -fsSL -o "${tftp_dir}/${ipxe_efi_filename}" "${ipxe_efi_url}"
|
||||
fi
|
||||
if [[ -n "${ipxe_efi_sha256}" ]]; then
|
||||
check_sha256 "${tftp_dir}/${ipxe_efi_filename}" "${ipxe_efi_sha256}"
|
||||
fi
|
||||
cp -f "${http_dir}/netboot.ipxe" "${tftp_dir}/netboot.ipxe"
|
||||
|
||||
cat > "${tftp_dir}/bootstrap.ipxe" <<'EOF'
|
||||
chain_url="http://${netboot_hostname}:${http_port}/netboot.ipxe"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
chain_url="${chain_url}?token=${chain_token}"
|
||||
fi
|
||||
|
||||
cat > "${tftp_dir}/bootstrap.ipxe" <<EOF
|
||||
#!ipxe
|
||||
dhcp
|
||||
chain http://__NETBOOT_HOST__:__HTTP_PORT__/netboot.ipxe
|
||||
chain ${chain_url}
|
||||
EOF
|
||||
sed -i.bak \
|
||||
-e "s#__NETBOOT_HOST__#${netboot_hostname}#g" \
|
||||
-e "s#__HTTP_PORT__#${http_port}#g" \
|
||||
"${tftp_dir}/bootstrap.ipxe"
|
||||
rm -f "${tftp_dir}/bootstrap.ipxe.bak"
|
||||
|
||||
echo "ok: staged netboot content"
|
||||
echo "ok: http root: ${http_dir}"
|
||||
echo "ok: tftp root: ${tftp_dir}"
|
||||
echo "ok: boot filename: ${ipxe_efi_filename}"
|
||||
echo "ok: netboot hostname: ${netboot_hostname}"
|
||||
echo "ok: netboot http port: ${http_port}"
|
||||
if [[ -n "${release_asset_url}" ]]; then
|
||||
|
|
@ -127,4 +240,7 @@ if [[ -n "${release_asset_url}" ]]; then
|
|||
else
|
||||
echo "ok: source asset: ${local_tarball}"
|
||||
fi
|
||||
echo "hint: run sudo ./scripts/netboot-serve.sh to expose HTTP+TFTP+ProxyDHCP"
|
||||
if [[ -n "${chain_token}" ]]; then
|
||||
echo "ok: chain token enabled"
|
||||
fi
|
||||
echo "hint: run sudo ./scripts/netboot-serve.sh to expose HTTP+TFTP (+ optional ProxyDHCP)"
|
||||
|
|
|
|||
127
scripts/op-stack/anvil-reality-smoke.sh
Executable file
127
scripts/op-stack/anvil-reality-smoke.sh
Executable file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
remote="${EVERY_CHANNEL_REALITY_SMOKE_REMOTE:-root@git.every.channel}"
|
||||
remote_jsonl="${EVERY_CHANNEL_REALITY_SMOKE_JSONL:-/var/lib/every-channel/manifests/la-cbs/video0.m4s.jsonl}"
|
||||
remote_identity_file="${EVERY_CHANNEL_REALITY_SMOKE_SSH_IDENTITY_FILE:-${HOME}/.ssh/id_ed25519}"
|
||||
anvil_port="${EVERY_CHANNEL_REALITY_SMOKE_ANVIL_PORT:-8545}"
|
||||
results_dir="${EVERY_CHANNEL_REALITY_SMOKE_RESULTS_DIR:-./test-results}"
|
||||
|
||||
anvil_pk0="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
||||
anvil_pk1="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
|
||||
anvil_pk2="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
|
||||
|
||||
mkdir -p "${results_dir}"
|
||||
anvil_log="${results_dir}/anvil-reality-smoke.log"
|
||||
deploy_json="${results_dir}/observation-ledger-deploy.json"
|
||||
result_json="${results_dir}/anvil-reality-smoke.json"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${anvil_pid:-}" ]]; then
|
||||
kill "${anvil_pid}" >/dev/null 2>&1 || true
|
||||
wait "${anvil_pid}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
anvil --port "${anvil_port}" >"${anvil_log}" 2>&1 &
|
||||
anvil_pid=$!
|
||||
|
||||
for _ in $(seq 1 30); do
|
||||
if curl -fsS -X POST -H 'content-type: application/json' \
|
||||
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
|
||||
"http://127.0.0.1:${anvil_port}" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
rpc_url="http://127.0.0.1:${anvil_port}"
|
||||
owner_file="$(mktemp)"
|
||||
printf '%s\n' "${anvil_pk0}" >"${owner_file}"
|
||||
|
||||
EVERY_CHANNEL_RPC_URL="${rpc_url}" \
|
||||
EVERY_CHANNEL_PRIVATE_KEY_FILE="${owner_file}" \
|
||||
EVERY_CHANNEL_OBSERVATION_DEPLOY_OUT="${deploy_json}" \
|
||||
./scripts/op-stack/deploy-observation-ledger.sh >/dev/null
|
||||
|
||||
registry="$(jq -r '.registry' <"${deploy_json}")"
|
||||
ledger="$(jq -r '.ledger' <"${deploy_json}")"
|
||||
|
||||
witness1="$(cast wallet address --private-key "${anvil_pk1}")"
|
||||
witness2="$(cast wallet address --private-key "${anvil_pk2}")"
|
||||
|
||||
cast send "${registry}" "addWitness(address)" "${witness1}" --rpc-url "${rpc_url}" --private-key "${anvil_pk0}" >/dev/null
|
||||
cast send "${registry}" "addWitness(address)" "${witness2}" --rpc-url "${rpc_url}" --private-key "${anvil_pk0}" >/dev/null
|
||||
|
||||
remote_jsonl_escaped="$(printf '%q' "${remote_jsonl}")"
|
||||
entry="$(ssh \
|
||||
-i "${remote_identity_file}" \
|
||||
-o IdentityAgent=none \
|
||||
-o IdentitiesOnly=yes \
|
||||
-o BatchMode=yes \
|
||||
-o ConnectTimeout=10 \
|
||||
"${remote}" \
|
||||
"head -n 1 -- ${remote_jsonl_escaped}")"
|
||||
if [[ -z "${entry}" ]]; then
|
||||
echo "no remote archive entry found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
broadcast_name="$(jq -r '.broadcast_name' <<<"${entry}")"
|
||||
track_name="$(jq -r '.track_name' <<<"${entry}")"
|
||||
group_sequence="$(jq -r '.group_sequence' <<<"${entry}")"
|
||||
received_unix_ms="$(jq -r '.received_unix_ms' <<<"${entry}")"
|
||||
blake3_hex="$(jq -r '.blake3' <<<"${entry}")"
|
||||
locator_material="$(jq -c '{relay_url,broadcast_name,track_name,group_sequence,cas_path}' <<<"${entry}")"
|
||||
|
||||
stream_hash="$(cast keccak "${broadcast_name}:${track_name}")"
|
||||
epoch_hash="$(cast keccak "${broadcast_name}:${track_name}:${group_sequence}")"
|
||||
parent_hash="0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
data_root="0x${blake3_hex}"
|
||||
locator_hash="$(cast keccak "${locator_material}")"
|
||||
|
||||
observation_tuple="(${stream_hash},${epoch_hash},${parent_hash},${data_root},${locator_hash},${received_unix_ms},${group_sequence})"
|
||||
cast send "${ledger}" \
|
||||
"proposeObservation((bytes32,bytes32,bytes32,bytes32,bytes32,uint64,uint64))" \
|
||||
"${observation_tuple}" \
|
||||
--rpc-url "${rpc_url}" \
|
||||
--private-key "${anvil_pk1}" >/dev/null
|
||||
|
||||
observation_hash="$(cast call "${ledger}" \
|
||||
"hashObservationHeader((bytes32,bytes32,bytes32,bytes32,bytes32,uint64,uint64))(bytes32)" \
|
||||
"${observation_tuple}" \
|
||||
--rpc-url "${rpc_url}")"
|
||||
slot_hash="$(cast call "${ledger}" \
|
||||
"observationSlot(bytes32,bytes32)(bytes32)" \
|
||||
"${stream_hash}" "${epoch_hash}" \
|
||||
--rpc-url "${rpc_url}")"
|
||||
|
||||
cast send "${ledger}" "attestObservation(bytes32)" "${observation_hash}" \
|
||||
--rpc-url "${rpc_url}" \
|
||||
--private-key "${anvil_pk2}" >/dev/null
|
||||
|
||||
finalized="$(cast call "${ledger}" "finalizedObservationBySlot(bytes32)(bytes32)" "${slot_hash}" --rpc-url "${rpc_url}")"
|
||||
|
||||
cat >"${result_json}" <<EOF
|
||||
{
|
||||
"remote": "${remote}",
|
||||
"remote_jsonl": "${remote_jsonl}",
|
||||
"broadcast_name": "${broadcast_name}",
|
||||
"track_name": "${track_name}",
|
||||
"group_sequence": ${group_sequence},
|
||||
"stream_hash": "${stream_hash}",
|
||||
"epoch_hash": "${epoch_hash}",
|
||||
"observation_hash": "${observation_hash}",
|
||||
"finalized_hash": "${finalized}",
|
||||
"registry": "${registry}",
|
||||
"ledger": "${ledger}"
|
||||
}
|
||||
EOF
|
||||
|
||||
if [[ "${finalized}" != "${observation_hash}" ]]; then
|
||||
echo "observation did not finalize" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "${result_json}"
|
||||
46
scripts/op-stack/deploy-observation-ledger.sh
Executable file
46
scripts/op-stack/deploy-observation-ledger.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
rpc_url="${EVERY_CHANNEL_RPC_URL:?set EVERY_CHANNEL_RPC_URL}"
|
||||
private_key_file="${EVERY_CHANNEL_PRIVATE_KEY_FILE:?set EVERY_CHANNEL_PRIVATE_KEY_FILE}"
|
||||
quorum="${EVERY_CHANNEL_OBSERVATION_QUORUM:-2}"
|
||||
out="${EVERY_CHANNEL_OBSERVATION_DEPLOY_OUT:-./test-results/observation-ledger-deploy.json}"
|
||||
|
||||
private_key="$(tr -d '\r\n' <"${private_key_file}")"
|
||||
private_key="${private_key#0x}"
|
||||
|
||||
mkdir -p "$(dirname "${out}")"
|
||||
|
||||
forge build >/dev/null
|
||||
|
||||
owner="$(cast wallet address --private-key "0x${private_key}")"
|
||||
|
||||
registry_output="$(forge create contracts/EveryChannelWitnessRegistry.sol:EveryChannelWitnessRegistry \
|
||||
--rpc-url "${rpc_url}" \
|
||||
--private-key "0x${private_key}" \
|
||||
--broadcast \
|
||||
--constructor-args "${owner}")"
|
||||
registry_address="$(printf '%s\n' "${registry_output}" | awk '/Deployed to:/ { print $3 }' | tail -n1)"
|
||||
|
||||
ledger_output="$(forge create contracts/EveryChannelObservationLedger.sol:EveryChannelObservationLedger \
|
||||
--rpc-url "${rpc_url}" \
|
||||
--private-key "0x${private_key}" \
|
||||
--broadcast \
|
||||
--constructor-args "${registry_address}" "${quorum}")"
|
||||
ledger_address="$(printf '%s\n' "${ledger_output}" | awk '/Deployed to:/ { print $3 }' | tail -n1)"
|
||||
|
||||
if [[ -z "${registry_address}" || -z "${ledger_address}" ]]; then
|
||||
echo "failed to deploy contracts" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat >"${out}" <<EOF
|
||||
{
|
||||
"owner": "${owner}",
|
||||
"registry": "${registry_address}",
|
||||
"ledger": "${ledger_address}",
|
||||
"quorum": ${quorum}
|
||||
}
|
||||
EOF
|
||||
|
||||
printf '%s\n' "${out}"
|
||||
49
scripts/op-stack/download-op-deployer.sh
Executable file
49
scripts/op-stack/download-op-deployer.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
tag="${EVERY_CHANNEL_OP_DEPLOYER_TAG:-op-deployer/v0.6.0-rc.3}"
|
||||
out="${EVERY_CHANNEL_OP_DEPLOYER_OUT:-./op-deployer}"
|
||||
|
||||
log() {
|
||||
printf '[op-deployer] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
detect_platform() {
|
||||
local os arch
|
||||
case "$(uname -s)" in
|
||||
Darwin) os="darwin" ;;
|
||||
Linux) os="linux" ;;
|
||||
*) echo "unsupported-os" ; return 1 ;;
|
||||
esac
|
||||
case "$(uname -m)" in
|
||||
aarch64|arm64) arch="arm64" ;;
|
||||
x86_64|amd64) arch="amd64" ;;
|
||||
*) echo "unsupported-arch" ; return 1 ;;
|
||||
esac
|
||||
printf '%s-%s\n' "$os" "$arch"
|
||||
}
|
||||
|
||||
platform="$(detect_platform)"
|
||||
if [[ "$platform" == unsupported-* ]]; then
|
||||
log "unsupported platform: $(uname -s)/$(uname -m)"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
version="${tag#op-deployer/v}"
|
||||
archive="op-deployer-${version}-${platform}.tar.gz"
|
||||
url="https://github.com/ethereum-optimism/optimism/releases/download/${tag}/${archive}"
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
log "downloading ${tag} for ${platform}"
|
||||
curl -fsSL "$url" -o "${tmpdir}/archive.tar.gz"
|
||||
tar -xzf "${tmpdir}/archive.tar.gz" -C "$tmpdir"
|
||||
|
||||
candidate="$(find "$tmpdir" -type f -name 'op-deployer*' -perm -u+x | head -n 1)"
|
||||
if [[ -z "$candidate" ]]; then
|
||||
log "failed to locate extracted op-deployer binary"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -m 0755 "$candidate" "$out"
|
||||
log "wrote ${out}"
|
||||
245
scripts/op-stack/setup-rollup.sh
Executable file
245
scripts/op-stack/setup-rollup.sh
Executable file
|
|
@ -0,0 +1,245 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root="${EVERY_CHANNEL_OP_STACK_ROOT:?set EVERY_CHANNEL_OP_STACK_ROOT}"
|
||||
private_key_file="${EVERY_CHANNEL_OP_STACK_PRIVATE_KEY_FILE:?set EVERY_CHANNEL_OP_STACK_PRIVATE_KEY_FILE}"
|
||||
l1_rpc_url="${EVERY_CHANNEL_OP_STACK_L1_RPC_URL:-https://ethereum-sepolia-rpc.publicnode.com}"
|
||||
l1_beacon_url="${EVERY_CHANNEL_OP_STACK_L1_BEACON_URL:-https://ethereum-sepolia-beacon-api.publicnode.com}"
|
||||
chain_id="${EVERY_CHANNEL_OP_STACK_CHAIN_ID:-245245}"
|
||||
p2p_advertise_ip="${EVERY_CHANNEL_OP_STACK_P2P_ADVERTISE_IP:-127.0.0.1}"
|
||||
op_deployer_bin="${EVERY_CHANNEL_OP_DEPLOYER_BIN:-${root}/bin/op-deployer}"
|
||||
download_script="${EVERY_CHANNEL_OP_DEPLOYER_DOWNLOAD_SCRIPT:-}"
|
||||
download_tag="${EVERY_CHANNEL_OP_DEPLOYER_TAG:-op-deployer/v0.6.0-rc.3}"
|
||||
skip_apply="${EVERY_CHANNEL_OP_STACK_SKIP_APPLY:-false}"
|
||||
challenger_prestate_file="${EVERY_CHANNEL_OP_STACK_CHALLENGER_PRESTATE_FILE:-}"
|
||||
|
||||
deployer_dir="${root}/deployer"
|
||||
sequencer_dir="${root}/sequencer"
|
||||
batcher_dir="${root}/batcher"
|
||||
proposer_dir="${root}/proposer"
|
||||
challenger_dir="${root}/challenger"
|
||||
dispute_mon_dir="${root}/dispute-mon"
|
||||
challenger_prestate_name=""
|
||||
|
||||
log() {
|
||||
printf '[op-stack] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
log "missing required command: $1"
|
||||
exit 2
|
||||
}
|
||||
}
|
||||
|
||||
trimmed_file_contents() {
|
||||
tr -d '\r\n' <"$1"
|
||||
}
|
||||
|
||||
set_toml_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local file="$3"
|
||||
if ! grep -q "^${key} = " "$file"; then
|
||||
log "missing key ${key} in ${file}"
|
||||
exit 1
|
||||
fi
|
||||
python - "$key" "$value" "$file" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
key, value, path = sys.argv[1:]
|
||||
text = Path(path).read_text()
|
||||
needle = f"{key} = "
|
||||
out = []
|
||||
replaced = False
|
||||
for line in text.splitlines():
|
||||
if line.startswith(needle):
|
||||
out.append(f'{key} = "{value}"')
|
||||
replaced = True
|
||||
else:
|
||||
out.append(line)
|
||||
if not replaced:
|
||||
raise SystemExit(f"failed to replace {key}")
|
||||
Path(path).write_text("\n".join(out) + "\n")
|
||||
PY
|
||||
}
|
||||
|
||||
mkdir -p "${root}/bin" "$deployer_dir" "$sequencer_dir" "$batcher_dir" "$proposer_dir" \
|
||||
"$challenger_dir" "$challenger_dir/data" "$dispute_mon_dir"
|
||||
|
||||
require_cmd cast
|
||||
require_cmd curl
|
||||
require_cmd jq
|
||||
require_cmd openssl
|
||||
|
||||
if [[ ! -x "$op_deployer_bin" ]]; then
|
||||
if [[ -z "$download_script" ]]; then
|
||||
log "op-deployer missing and no download script configured"
|
||||
exit 2
|
||||
fi
|
||||
log "downloading op-deployer via ${download_script}"
|
||||
EVERY_CHANNEL_OP_DEPLOYER_TAG="$download_tag" \
|
||||
EVERY_CHANNEL_OP_DEPLOYER_OUT="$op_deployer_bin" \
|
||||
"$download_script"
|
||||
fi
|
||||
|
||||
private_key="$(trimmed_file_contents "$private_key_file")"
|
||||
private_key="${private_key#0x}"
|
||||
if [[ ${#private_key} -ne 64 ]]; then
|
||||
log "private key file must contain exactly 32 bytes of hex"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
operator_address="$(cast wallet address --private-key "0x${private_key}")"
|
||||
|
||||
cat > "${deployer_dir}/.env" <<EOF
|
||||
L1_RPC_URL=${l1_rpc_url}
|
||||
PRIVATE_KEY=${private_key}
|
||||
EOF
|
||||
|
||||
if [[ ! -d "${deployer_dir}/.deployer" ]]; then
|
||||
"$op_deployer_bin" init \
|
||||
--l1-chain-id 11155111 \
|
||||
--l2-chain-ids "${chain_id}" \
|
||||
--workdir "${deployer_dir}/.deployer" \
|
||||
--intent-type standard-overrides
|
||||
fi
|
||||
|
||||
intent="${deployer_dir}/.deployer/intent.toml"
|
||||
chain_id_hex="$(printf '0x%064x' "${chain_id}")"
|
||||
set_toml_value "id" "${chain_id_hex}" "${intent}"
|
||||
for key in \
|
||||
baseFeeVaultRecipient \
|
||||
l1FeeVaultRecipient \
|
||||
sequencerFeeVaultRecipient \
|
||||
operatorFeeVaultRecipient \
|
||||
chainFeesRecipient \
|
||||
systemConfigOwner \
|
||||
unsafeBlockSigner \
|
||||
batcher \
|
||||
proposer \
|
||||
challenger
|
||||
do
|
||||
set_toml_value "${key}" "${operator_address}" "${intent}"
|
||||
done
|
||||
|
||||
python - "${intent}" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
path = Path(sys.argv[1])
|
||||
text = path.read_text()
|
||||
if 'fundDevAccounts = false' not in text:
|
||||
text = text.replace('fundDevAccounts = true', 'fundDevAccounts = false')
|
||||
path.write_text(text)
|
||||
PY
|
||||
|
||||
if [[ "${skip_apply}" != "true" && ! -f "${deployer_dir}/.deployer/state.json" ]]; then
|
||||
"$op_deployer_bin" apply \
|
||||
--workdir "${deployer_dir}/.deployer" \
|
||||
--l1-rpc-url "${l1_rpc_url}" \
|
||||
--private-key "${private_key}"
|
||||
fi
|
||||
|
||||
if [[ ! -f "${deployer_dir}/.deployer/state.json" ]]; then
|
||||
log "state.json missing; bootstrap did not complete"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$op_deployer_bin" inspect genesis --workdir "${deployer_dir}/.deployer" "${chain_id_hex}" >"${sequencer_dir}/genesis.json"
|
||||
"$op_deployer_bin" inspect rollup --workdir "${deployer_dir}/.deployer" "${chain_id_hex}" >"${sequencer_dir}/rollup.json"
|
||||
openssl rand -hex 32 >"${sequencer_dir}/jwt.txt"
|
||||
chmod 0600 "${sequencer_dir}/jwt.txt"
|
||||
|
||||
state_json="${deployer_dir}/.deployer/state.json"
|
||||
system_config_proxy="$(jq -r '.opChainDeployments[0].systemConfigProxyAddress // .opChainDeployments[0].SystemConfigProxy // empty' <"${state_json}")"
|
||||
dispute_game_factory="$(jq -r '.opChainDeployments[0].disputeGameFactoryProxyAddress // .opChainDeployments[0].DisputeGameFactoryProxy // empty' <"${state_json}")"
|
||||
l1_standard_bridge="$(jq -r '.opChainDeployments[0].l1StandardBridgeProxyAddress // .opChainDeployments[0].L1StandardBridgeProxy // empty' <"${state_json}")"
|
||||
|
||||
if [[ -z "${system_config_proxy}" || "${system_config_proxy}" == "null" ]]; then
|
||||
log "failed to extract systemConfigProxyAddress from state.json"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${dispute_game_factory}" || "${dispute_game_factory}" == "null" ]]; then
|
||||
log "failed to extract disputeGameFactoryProxyAddress from state.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${challenger_prestate_file}" ]]; then
|
||||
if [[ ! -f "${challenger_prestate_file}" ]]; then
|
||||
log "challenger prestate file does not exist: ${challenger_prestate_file}"
|
||||
exit 1
|
||||
fi
|
||||
challenger_prestate_name="$(basename "${challenger_prestate_file}")"
|
||||
cp "${challenger_prestate_file}" "${challenger_dir}/${challenger_prestate_name}"
|
||||
chmod 0640 "${challenger_dir}/${challenger_prestate_name}"
|
||||
fi
|
||||
|
||||
cat > "${sequencer_dir}/.env" <<EOF
|
||||
L1_RPC_URL=${l1_rpc_url}
|
||||
L1_BEACON_URL=${l1_beacon_url}
|
||||
PRIVATE_KEY=${private_key}
|
||||
P2P_ADVERTISE_IP=${p2p_advertise_ip}
|
||||
L2_CHAIN_ID=${chain_id}
|
||||
EOF
|
||||
|
||||
cat > "${batcher_dir}/.env" <<EOF
|
||||
L1_RPC_URL=${l1_rpc_url}
|
||||
L2_RPC_URL=http://127.0.0.1:8545
|
||||
ROLLUP_RPC_URL=http://127.0.0.1:8547
|
||||
PRIVATE_KEY=${private_key}
|
||||
BATCH_INBOX_ADDRESS=${system_config_proxy}
|
||||
EOF
|
||||
|
||||
cat > "${proposer_dir}/.env" <<EOF
|
||||
L1_RPC_URL=${l1_rpc_url}
|
||||
ROLLUP_RPC_URL=http://127.0.0.1:8547
|
||||
GAME_FACTORY_ADDRESS=${dispute_game_factory}
|
||||
PRIVATE_KEY=${private_key}
|
||||
PROPOSAL_INTERVAL=3600s
|
||||
EOF
|
||||
|
||||
cp "${sequencer_dir}/genesis.json" "${challenger_dir}/genesis.json"
|
||||
cp "${sequencer_dir}/rollup.json" "${challenger_dir}/rollup.json"
|
||||
cat > "${challenger_dir}/.env" <<EOF
|
||||
L1_RPC_URL=${l1_rpc_url}
|
||||
L1_BEACON_URL=${l1_beacon_url}
|
||||
L2_RPC_URL=http://127.0.0.1:8545
|
||||
ROLLUP_RPC_URL=http://127.0.0.1:8547
|
||||
GAME_FACTORY_ADDRESS=${dispute_game_factory}
|
||||
PRIVATE_KEY=${private_key}
|
||||
EOF
|
||||
|
||||
if [[ -n "${challenger_prestate_name}" ]]; then
|
||||
cat >> "${challenger_dir}/.env" <<EOF
|
||||
CANNON_PRESTATE=/workspace/${challenger_prestate_name}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat > "${dispute_mon_dir}/.env" <<EOF
|
||||
OP_DISPUTE_MON_L1_ETH_RPC=${l1_rpc_url}
|
||||
OP_DISPUTE_MON_ROLLUP_RPC=http://127.0.0.1:8547
|
||||
OP_DISPUTE_MON_HONEST_ACTORS=${operator_address}
|
||||
OP_DISPUTE_MON_GAME_FACTORY_ADDRESS=${dispute_game_factory}
|
||||
OP_DISPUTE_MON_MONITOR_INTERVAL=10s
|
||||
OP_DISPUTE_MON_NETWORK=ecp-sepolia
|
||||
OP_DISPUTE_MON_LOG_LEVEL=debug
|
||||
OP_DISPUTE_MON_LOG_FORMAT=logfmt
|
||||
OP_DISPUTE_MON_METRICS_ADDR=0.0.0.0
|
||||
OP_DISPUTE_MON_METRICS_ENABLED=true
|
||||
OP_DISPUTE_MON_METRICS_PORT=7300
|
||||
EOF
|
||||
|
||||
cat > "${root}/deployment.json" <<EOF
|
||||
{
|
||||
"chain_id": ${chain_id},
|
||||
"operator_address": "${operator_address}",
|
||||
"l1_rpc_url": "${l1_rpc_url}",
|
||||
"l1_beacon_url": "${l1_beacon_url}",
|
||||
"p2p_advertise_ip": "${p2p_advertise_ip}",
|
||||
"system_config_proxy": "${system_config_proxy}",
|
||||
"dispute_game_factory": "${dispute_game_factory}",
|
||||
"l1_standard_bridge": "${l1_standard_bridge}"
|
||||
}
|
||||
EOF
|
||||
|
||||
log "wrote bootstrap outputs under ${root}"
|
||||
|
|
@ -3,9 +3,15 @@ let
|
|||
founder = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCBTSEEcBOhOkf3WF1e8xmblAZHvgTibFsqck2GY8D/";
|
||||
# Forge automation SSH public key (recipient). Safe to commit.
|
||||
forge = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmKJt5+uilix5Ldiaaq1BhrYNjmV5lHcW7D/5inCCnO forge@every.channel";
|
||||
# ecp-forge host SSH key (recipient) so NixOS can decrypt runtime secrets locally.
|
||||
ecpForgeHost = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFtifu+ktG7rBZgI7wlAzsaSkaX/PtPy22SThB2wKw3A root@ecp-forge";
|
||||
in
|
||||
{
|
||||
"secrets/cloudflare-api-token.age".publicKeys = [ founder forge ];
|
||||
"secrets/forge-token.age".publicKeys = [ founder forge ];
|
||||
"secrets/codeberg-token.age".publicKeys = [ founder forge ];
|
||||
"secrets/forgejo-api-token.age".publicKeys = [ founder forge ecpForgeHost ];
|
||||
"secrets/netboot-chain-token.age".publicKeys = [ founder forge ecpForgeHost ];
|
||||
"secrets/op-stack-sepolia-private-key.age".publicKeys = [ founder forge ecpForgeHost ];
|
||||
"secrets/op-stack-challenger-prestate.bin.gz.age".publicKeys = [ founder forge ecpForgeHost ];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,14 @@ nix develop -c ./scripts/fj-set-age-key-secret.sh ~/.config/every.channel/keys/f
|
|||
|
||||
## Files
|
||||
|
||||
- `secrets/secrets.nix`: recipients + secret file mapping
|
||||
- `secrets.nix`: recipients + secret file mapping
|
||||
- `secrets/cloudflare-api-token.age`: encrypted Cloudflare API token (used by deploy workflow)
|
||||
- `secrets/forge-token.age`: encrypted Forgejo API token for admin scripts (optional, preferred)
|
||||
- `secrets/forgejo-api-token.age`: encrypted Forgejo API token (preferred) for admin scripts and `ecp-forge` netboot staging
|
||||
- `secrets/forge-token.age`: legacy encrypted Forgejo API token path for compatibility
|
||||
- `secrets/codeberg-token.age`: encrypted Codeberg token for compatibility/mirror admin scripts (optional)
|
||||
- `secrets/netboot-chain-token.age`: encrypted chain token used by `services.every-channel.netboot.chainTokenFile`
|
||||
- `secrets/op-stack-sepolia-private-key.age`: encrypted Sepolia operator private key used by `services.every-channel.op-stack.privateKeyFile`
|
||||
- `secrets/op-stack-challenger-prestate.bin.gz.age`: encrypted Cannon absolute prestate artifact used by `services.every-channel.op-stack.challengerPrestateFile`
|
||||
|
||||
## Create / edit secrets (local)
|
||||
|
||||
|
|
|
|||
12
secrets/forgejo-api-token.age
Normal file
12
secrets/forgejo-api-token.age
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDI5T0o0QSBGa1Er
|
||||
QXNoaTRTTWZ0V3RVMEhyd1ZLL0ZpeHU0SHZWU00vT2pZZFNJa0dZCjNkL1FENnNu
|
||||
QWtkYVBvZ05BWi9nQnJiS2R4MmFSWEpndlFZbG9XZE8xTG8KLT4gc3NoLWVkMjU1
|
||||
MTkgRTZRK0x3IEI3b0R4ZEdhZEUrQ1VTRVdGT3Z6ZVlGS3JaVmsyakJDU01VZDc3
|
||||
U0FuRE0KTkpQYWw2aUpaemNqZnl2OTRjdlVrNWdLQ0JrUXRrQzRVeXFMaHd6OGR5
|
||||
SQotPiBzc2gtZWQyNTUxOSB1S0hwNHcgR08wZWdnYWtGOWhmNTAxOVZmNnBxVjN4
|
||||
cjFGQ1IwdFdmbjFlc2o0MU5DbwpoOG1jblVOOUQrOS9qd1MzdVdKSXRaOTJpSUF6
|
||||
SXZJUVNoMEI5SFc2RzRzCi0tLSBlbWJ6SUVJQjlvaU4zb2hkbFZUSmN6OTVGa2dG
|
||||
ZmxKd0VHZ3Bpc0kxMnVZCl4tmozvALN44xto0FxpO9XDzzMkKnU+hxvDNCIAbp38
|
||||
j/hqviNT1QHSVW4WJLhmVxgqevnEW0PUsWCG2idJRHJ94bPLxUsD9o4=
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
13
secrets/netboot-chain-token.age
Normal file
13
secrets/netboot-chain-token.age
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDI5T0o0QSBHVzJa
|
||||
ZTEzbVR6UXczVVlUaGJXUlBINCtxbTV6NHRMdmdZa3BFalhab0hzCkFMQjd0V29D
|
||||
eVJaa2RneGRuSFgvZmlRUk94a09HTjZtUlBBUklwYzRvUnMKLT4gc3NoLWVkMjU1
|
||||
MTkgRTZRK0x3IGxvZzQ4clpKc3p3Y0doU09BbVJYL1Fmcy83L2dIdjBNalpnRXBW
|
||||
RTlhVTQKQWRRYmhBSElPUmhzTVZUNENBdUZReFpWTFhTQVduY0NTVXJxSDRrcmJp
|
||||
cwotPiBzc2gtZWQyNTUxOSB1S0hwNHcgTGxpaXNpems0S0pBQ3c4V1lOZStTb2Rz
|
||||
Y1ZFSDczZnNRZWNYSE91SHJ5SQpWTDZDYlZxYTliRkFGK0MxNHpLNWxhcGhhUGZB
|
||||
em9jVUxKR01wNi9PZEtJCi0tLSBqQWNBU1NPSzlVSHZLYVdDRVBKdUlEVjJ1SGY5
|
||||
dHd3bjRTRjBYVjU3ZmFrCv+MVi3tXJP4TVVFRd1BWBtK9a+jxID9MiQg63cNRgRc
|
||||
rwkJQTm8W5bc3T0Z4VvmIOBlUVr5dUFpdTTL1ieIELUPc9rUA0dPtGi82jFLKwWg
|
||||
3g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
9
secrets/op-stack-sepolia-private-key.age
Normal file
9
secrets/op-stack-sepolia-private-key.age
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 29OJ4A ePeU9yk5rIUa4eY6YPZxfJwOm9UuknusEVlDFKjG5yk
|
||||
INGbIePGXyq2hVLN0nzWnzQfGc9FTjot/owrtfSb14g
|
||||
-> ssh-ed25519 E6Q+Lw UGKrcPeqrpOYzV2av7IuItfjj/tR6Q4iJ6EejJdpjlA
|
||||
i/9AHEsIZ1ynza0ZNd2Jzq/zceGxlnA6KFcZ1yFSiy4
|
||||
-> ssh-ed25519 uKHp4w PCl6trn5v4oZ2RU1Ef6tVB/9dacPwGpJnQ6DCTO9mw0
|
||||
gCfwb4sW5CoEO951Z9ALAXmckchTIitU9rLHK+Gvq88
|
||||
--- mecoDuf+Ew7R8jne3RdpQwa/Y0fEcb1jbUALv+zA9sg
|
||||
‰'¾<>Qõnê$ñ§!H6`rüI#_ûùÆLFýÝDq•àS˜9 åPµôeÌwnÜ1Oû1$é›:œö?ç1´8ËÂdR»„}‰ò¦ô„TU¤–T±Ÿ`ÏOÂþÏ
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use iroh::{EndpointAddr, endpoint::{ConnectOptions, QuicTransportConfig}};
|
||||
use iroh::{
|
||||
EndpointAddr,
|
||||
endpoint::{ConnectOptions, QuicTransportConfig},
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::{ALPN_H3, ClientError, Session};
|
||||
|
|
@ -17,10 +20,7 @@ impl Client {
|
|||
}
|
||||
|
||||
/// Creates a client from an endpoint and a transport config.
|
||||
pub fn with_transport_config(
|
||||
endpoint: iroh::Endpoint,
|
||||
config: QuicTransportConfig,
|
||||
) -> Self {
|
||||
pub fn with_transport_config(endpoint: iroh::Endpoint, config: QuicTransportConfig) -> Self {
|
||||
Self { endpoint, config }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ impl RecvStream {
|
|||
&mut self,
|
||||
max_length: usize,
|
||||
) -> Result<Option<quinn::Chunk>, ReadError> {
|
||||
self.inner
|
||||
.read_chunk(max_length)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
self.inner.read_chunk(max_length).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Read chunks of data from the stream. See [`quinn::RecvStream::read_chunks`].
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue