fix(rebrand): complete the conversion the rg-based checks missed

A `git grep` audit (which, unlike `rg`, does not skip dotfiles) found the sweep
was incomplete in ways four green CI runs could not see.

**Dotfiles were never in the rewrite lists.** The `/api/codebase` and
`codebase.design` fixes built their file list from `rg -l`, so `.env.example`
and `deploy/compose/.env.example` kept the old control-plane path and an
escaped legacy host regex. `bin/git` was separately reverted to its
Codebase-era error text by the `git checkout -- bin/` that repaired the
clobbered Hermit symlinks.

**The N-1 hop was dropped in two more places**, the same defect as
codebaseChat-b8x, one generation on:

- `e2e_git.rs` published `buzz-channel` + `codebase-chat-channel` on its
  kind:30617 fixtures, per the expand-window procedure in the schema-rollout
  runbook. The sweep rewrote the second rather than adding a third, so the
  fixture stopped exercising the generation that is actually N-1 here. All
  three are now emitted. The relay still reads only its native tag, which is
  the documented design: writers emit every live generation, readers read one.
- The chart alert pack kept `buzz_*` fallbacks beside the then-current
  `codebase_chat_*` metrics. Renaming the current set to `meridian_*` left
  `buzz_*` — two generations back — as the only fallback, so a rolling upgrade
  would have matched neither on pods still emitting `codebase_chat_*`: exactly
  the observability blackout the fallback exists to prevent. The fallback now
  tracks the immediately-preceding generation, which is what "one retention
  window" in the chart README always meant. promtool passes.

**Sprout-era runtime config**, which is operator-facing rather than persisted,
now resolves through one `config::legacy_env_u64`/`_i64` helper that prefers
`MERIDIAN_*` and falls back to `SPROUT_*`, so existing environments keep
working. A single resolver is deliberate: `MAX_NOT_BEFORE_DELTA` is both
enforced in ingest and advertised in NIP-11, and those drifting apart is the
second-copy defect this rebrand keeps producing.

Also: workspace/persona `repository` metadata off `block/sprout`; the "I work
at Block" README section replaced with the honest no-packaged-builds note; the
Block corporate-CA keychain export in the local deploy script replaced with a
bring-your-own-bundle hatch; iOS `CFBundleURLName` and the theme/test locals
that my bead-ID revert had left spelled `codebaseChat`.

SECURITY.md carried **meridian@block.xyz**, which would have routed a report
about this deployment to a third party. Flagged in-file with a TODO rather than
silently pointed somewhere plausible.

Apache-2.0 copyright and attribution to Block, Inc. are retained deliberately
in LICENSE, ARCHITECTURE.md, the README footer, and the Goose logo credits —
the license requires it and a rename does not transfer authorship.

Verified: just ci exit 0; promtool SUCCESS; relay clippy -D warnings clean.
Signed-off-by: Joshua Belke <joshua@innovationhub-act.org>
This commit is contained in:
Josh Belke 2026-08-04 23:31:26 -04:00
commit c5c733d3ef
21 changed files with 142 additions and 75 deletions

View file

@ -187,7 +187,7 @@ RELAY_URL=ws://localhost:3000
# A packaged app launched from Finder, the Dock, or a deep link inherits no
# shell environment, so a build that must create communities has to set these at
# BUILD time — they are compiled in as a fallback to the runtime values.
# MERIDIAN_CONTROL_PLANE_URL=http://127.0.0.1:8090/api/codebase
# MERIDIAN_CONTROL_PLANE_URL=http://127.0.0.1:8090/api/meridian
# MERIDIAN_CONTROL_PLANE_ORIGIN=http://127.0.0.1:8090
# Shared Redis-backed admission limits. Defaults shown below; each value must

View file

@ -531,7 +531,7 @@ jobs:
MERIDIAN_RATE_LIMIT_HUMAN_API_CALLS_PER_MIN=100000 \
MERIDIAN_RATE_LIMIT_HUMAN_WS_EVENTS_PER_SEC=10000 \
MERIDIAN_GIT_PROBE_WRITERS=8 \
SPROUT_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
MERIDIAN_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
./target/ci/meridian-relay > /tmp/meridian-relay.log 2>&1 &
echo $! > /tmp/meridian-relay.pid
for attempt in $(seq 1 60); do
@ -689,7 +689,7 @@ jobs:
MERIDIAN_WORKFLOW_SECRET_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
MERIDIAN_RECONCILE_CHANNELS=true \
MERIDIAN_GIT_PROBE_WRITERS=8 \
SPROUT_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
MERIDIAN_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
./target/ci/meridian-relay > /tmp/meridian-relay.log 2>&1 &
echo $! > /tmp/meridian-relay.pid
for attempt in $(seq 1 60); do

View file

@ -645,11 +645,27 @@ self-hosted infrastructure rather than being renamed in place. 1826 files.
| Repo slug | `codebase/codebase-chat` | `r2d2/meridian` |
| Control-plane API base | `/api/codebase` | `/api/meridian` |
`sprout` is **not** swept. It is an older brand whose surviving references are
stored identifiers (`sprout_agent_provider`, `xyz.block.sprout.app`,
`sprout-workspaces`) plus the `sprig` crate. Renaming those breaks the same
migrations the rules below protect. Only the `sprout-cli` skill was renamed, to
`meridian-cli`, because it is tooling rather than stored state.
`sprout` is **mostly** not swept. Its surviving references are stored
identifiers (`sprout_agent_provider`, `xyz.block.sprout.app`,
`sprout-workspaces`); renaming those breaks the same migrations the rules below
protect. Three exceptions were converted because they are not stored state:
- the `sprout-cli` skill → `meridian-cli` (tooling, zero blast radius);
- `repository = "https://github.com/block/sprout"` in the workspace and
`meridian-persona` manifests → `r2d2/meridian`;
- the runtime env knobs `SPROUT_MAX_NOT_BEFORE_DELTA`,
`SPROUT_REMINDER_SCHEDULER_{INTERVAL_SECS,BATCH_LIMIT}`, which are
operator-facing configuration rather than persisted data. They now resolve
through `config::legacy_env_u64`/`_i64`, which reads `MERIDIAN_*` first and
falls back to `SPROUT_*`, so an existing environment keeps working. One
resolver rather than four inline reads is deliberate:
`MAX_NOT_BEFORE_DELTA` is both *enforced* in ingest and *advertised* in
NIP-11, and those two drifting apart is the classic second-copy defect.
Still carrying the Sprout lineage, and **an open decision**: the `sprig` crate
— the all-in-one ACP/agent/dev-MCP harness. "Sprig" is a Sprout diminutive, but
it names a shipped binary, a Docker image, and `.github/workflows/sprig.yml`,
so renaming it is an outward-facing change rather than a sweep.
### The symbol is now a pixel M — and its proportion is a constraint
@ -789,6 +805,35 @@ Recorded because each cost real time and none is obvious:
that shipped them; new vocabulary gets a new migration and a new assertion
block. This is the same defect as the retired-Fizz fixtures in § 2, one level
further out.
- **`rg` skips dotfiles; `git grep` does not.** Every *sweep* was correct
because it drove off `git ls-files`, but several targeted *fixes* built their
file list from `rg -l` — which silently excludes `.env.example`,
`deploy/compose/.env.example`, `.github/`, and every other dotpath. Two
config files kept `/api/codebase` and an escaped `codebase.design` through
four green CI runs because of this. **Audit with `git grep`**, and never
build a rewrite list from `rg` without `--hidden`.
- **Restoring a file from git can silently undo the sweep.**
`git checkout -- bin/` (repairing the clobbered Hermit symlinks) also reverted
`bin/git`, a real tracked script, back to its Codebase-era error message.
After any `git checkout` during a rename, re-audit the paths you restored.
- **The N-1 hop got dropped in three more places, each the same shape.** The
rule "every rename prepends, never replaces" is not limited to app-data
directories:
- `e2e_git.rs` published `buzz-channel` + `codebase-chat-channel` on its
kind:30617 fixtures, per the expand-window procedure in
`docs/runbooks/relay-schema-rollout.md`. The sweep rewrote the *second*
rather than adding a third, so the fixture stopped exercising the
generation that is actually N-1 for this rollout.
- The chart's alert pack carried `buzz_*` fallbacks beside the then-current
`codebase_chat_*` metrics. The sweep renamed the current set to
`meridian_*` and left `buzz_*` — two generations back — as the only
fallback, so a rolling upgrade would have matched neither on pods still
emitting `codebase_chat_*`. That is precisely the observability blackout
the fallback exists to prevent. Fixed by moving the fallback to the
immediately-preceding generation, which is what "one retention window" in
the chart README always meant.
- Migration `0031` had to be written to dual-read *all three* generations,
not just the newest two.
- **A shorter name reflows code and exposes latent lints.** `meridian_core` is
shorter than `codebase_chat_core`, so rustfmt collapsed a block-bodied closure
in `ingest.rs` onto one line — and `clippy::nonminimal_bool` only fires on the

View file

@ -37,7 +37,7 @@ version = "0.1.0"
edition = "2021"
rust-version = "1.88.0"
license = "Apache-2.0"
repository = "https://github.com/block/sprout"
repository = "https://github.com/r2d2/meridian"
[workspace.dependencies]
# Runtime

View file

@ -115,16 +115,13 @@ New to Meridian? Pick the path that matches you.
### I just want to try the app
Grab a packaged build from the [latest release](https://github.com/r2d2/meridian/releases/latest) — macOS (`.dmg`), Linux (`.AppImage` / `.deb`), or Windows (`.exe`). Install it like any other app.
> **No packaged builds are published yet.** The signed macOS/iOS pipeline has not
> been replaced for this self-hosted deployment — see
> [RELEASING.md § Signed platform builds](RELEASING.md). Until it is, build from
> source below.
By default the app connects to `ws://localhost:3000`. To point it at a relay you're running or one someone shared with you, set `MERIDIAN_RELAY_URL` before launching, or switch the relay from inside the app. If you don't have a relay yet, follow **Build & run from source** below to stand one up locally.
### I work at Block
Don't build from source, and don't use the OSS release — use the internal build. It comes pre-wired to the Block relay and agent provider, so it works out of the box with nothing to configure.
Download the latest build from [`squareup/meridian-releases` releases](https://github.com/squareup/meridian-releases/releases/latest) and install it.
### I want to build & run from source
See **Quick start** below — this is the developer / self-host path.

View file

@ -4,8 +4,14 @@
**Please do not report security vulnerabilities through public GitHub issues.**
> **⚠ The reporting address below is not yet configured for this deployment.**
> It previously routed to Block, which no longer operates this fork — sending a
> report there would disclose a vulnerability in *your* deployment to a third
> party. Set a real contact before publishing this repo anywhere.
If you discover a security vulnerability in Meridian, please report it by emailing
**meridian@block.xyz**. Include as much detail as possible:
**security@meridian.r2d2.office.ilab.zone** (TODO: confirm this mailbox exists
and is monitored). Include as much detail as possible:
- A description of the vulnerability and its potential impact
- Steps to reproduce or a proof-of-concept (if available)

View file

@ -37,7 +37,7 @@ version_at_least() {
}
if ! version_at_least "${installed_version}" "${minimum_version}"; then
echo "error: Git ${minimum_version} or newer is required for Codebase Chat Nostr authentication (found ${installed_version})" >&2
echo "error: Git ${minimum_version} or newer is required for Meridian Nostr authentication (found ${installed_version})" >&2
if [[ "$(uname -s)" == "Darwin" ]]; then
echo "Install a current Git (for example, 'brew install git'), then reactivate Hermit." >&2
fi

View file

@ -646,7 +646,7 @@ pub const MAX_TOOL_RESULT_BYTES: usize = 8 * 1024 * 1024;
/// Default cap on the *text* portion of a single tool result. Oversized text
/// is middle-elided before it enters history; without this, one fat `cat`
/// burns the context window and forces a lossy handoff. 50 KiB matches the
/// shell-output caps in sprout-dev-mcp, goose, and pi; codex defaults to
/// shell-output caps in meridian-dev-mcp, goose, and pi; codex defaults to
/// 10 KB. Tunable via `MERIDIAN_AGENT_MAX_TOOL_RESULT_TEXT_BYTES`.
pub const DEFAULT_TOOL_RESULT_TEXT_BYTES: usize = 50 * 1024;
pub const MAX_TOOL_CALLS_PER_TURN: usize = 64;

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
description = "Parser and loader for Meridian persona pack files (.persona.md)"
license = "Apache-2.0"
repository = "https://github.com/block/sprout"
repository = "https://github.com/r2d2/meridian"
[dependencies]
serde = { version = "1", features = ["derive"] }

View file

@ -1106,6 +1106,35 @@ impl Config {
}
}
/// Read a numeric setting from `MERIDIAN_<name>`, falling back to the retired
/// `SPROUT_<name>` spelling.
///
/// A handful of relay knobs were never renamed past the Sprout generation, so
/// an operator's existing environment still spells them `SPROUT_*`. Dropping
/// that spelling would silently revert the knob to its default rather than
/// fail — the same failure mode the GUC and app-data bridges exist to prevent —
/// so both are read, current name first.
///
/// This is the single resolver for these values on purpose:
/// `SPROUT_MAX_NOT_BEFORE_DELTA` is both *enforced* in the ingest path and
/// *advertised* in NIP-11, and those two must not drift apart.
pub fn legacy_env_u64(name: &str, default: u64) -> u64 {
std::env::var(format!("MERIDIAN_{name}"))
.or_else(|_| std::env::var(format!("SPROUT_{name}")))
.ok()
.and_then(|value| value.parse().ok())
.unwrap_or(default)
}
/// Signed variant of [`legacy_env_u64`].
pub fn legacy_env_i64(name: &str, default: i64) -> i64 {
std::env::var(format!("MERIDIAN_{name}"))
.or_else(|_| std::env::var(format!("SPROUT_{name}")))
.ok()
.and_then(|value| value.parse().ok())
.unwrap_or(default)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1341,11 +1341,9 @@ fn validate_event_reminder(event: &Event) -> Result<(), &'static str> {
// omit it. The ordering check only applies when both are present.
if let Some(nb) = not_before {
// Reject reminders scheduled beyond the configured horizon. The same
// SPROUT_MAX_NOT_BEFORE_DELTA env var is advertised in NIP-11.
let max_delta: u64 = std::env::var("SPROUT_MAX_NOT_BEFORE_DELTA")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(31_536_000); // 1 year default
// MAX_NOT_BEFORE_DELTA setting is advertised in NIP-11 — both read it
// through `config::legacy_env_u64`, so they cannot drift.
let max_delta = crate::config::legacy_env_u64("MAX_NOT_BEFORE_DELTA", 31_536_000); // 1 year default
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()

View file

@ -830,14 +830,10 @@ async fn main() -> anyhow::Result<()> {
// column: only the pod that wins the atomic claim publishes.
{
let scheduler_state = Arc::clone(&state);
let scheduler_interval_secs: u64 = std::env::var("SPROUT_REMINDER_SCHEDULER_INTERVAL_SECS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10);
let scheduler_batch_limit: i64 = std::env::var("SPROUT_REMINDER_SCHEDULER_BATCH_LIMIT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(100);
let scheduler_interval_secs =
meridian_relay::config::legacy_env_u64("REMINDER_SCHEDULER_INTERVAL_SECS", 10);
let scheduler_batch_limit =
meridian_relay::config::legacy_env_i64("REMINDER_SCHEDULER_BATCH_LIMIT", 100);
tokio::spawn(async move {
info!(
interval_secs = scheduler_interval_secs,

View file

@ -147,10 +147,7 @@ pub struct RelayLimitation {
/// `AuthState::Authenticated`. This is independent of the REST API token
/// toggle (`config.require_auth_token`).
fn relay_limitation(max_message_length: usize) -> RelayLimitation {
let max_not_before_delta: u64 = std::env::var("SPROUT_MAX_NOT_BEFORE_DELTA")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(31_536_000); // 1 year default
let max_not_before_delta = crate::config::legacy_env_u64("MAX_NOT_BEFORE_DELTA", 31_536_000); // 1 year default
RelayLimitation {
max_message_length: Some(max_message_length as u64),

View file

@ -307,6 +307,7 @@ async fn git_clone_push_fetch_force_roundtrip() {
// current N reads the ownership-neutral name. Both bind the same
// UUID, so there is one ACL fact and no synchronization shim.
Tag::parse(["buzz-channel", &channel]).unwrap(),
Tag::parse(["codebase-chat-channel", &channel]).unwrap(),
Tag::parse(["meridian-channel", &channel]).unwrap(),
])
.sign_with_keys(&owner)
@ -620,6 +621,7 @@ async fn git_pointer_rollback_is_quarantined_for_read_and_write() {
Tag::parse(["d", &repo]).unwrap(),
Tag::parse(["name", "rollback quarantine fixture"]).unwrap(),
Tag::parse(["buzz-channel", &channel]).unwrap(),
Tag::parse(["codebase-chat-channel", &channel]).unwrap(),
Tag::parse(["meridian-channel", &channel]).unwrap(),
])
.sign_with_keys(&owner)
@ -689,6 +691,7 @@ async fn git_concurrent_push_one_wins_and_repo_recovers() {
Tag::parse(["d", &repo]).unwrap(),
Tag::parse(["name", "e2e concurrent git repo"]).unwrap(),
Tag::parse(["buzz-channel", &channel]).unwrap(),
Tag::parse(["codebase-chat-channel", &channel]).unwrap(),
Tag::parse(["meridian-channel", &channel]).unwrap(),
])
.sign_with_keys(&owner)

View file

@ -243,7 +243,7 @@ default so long-lived WebSocket connections have time to drain.
Enable `serviceMonitor.enabled` and `prometheusRule.enabled` to scrape relay
metrics and install the bundled availability, dependency, and correctness
alerts. Every alert links to `docs/runbooks/relay-alerts.md`. The rule pack
keeps `buzz_*` fallbacks beside renamed `meridian_*` metrics for one
keeps `codebase_chat_*` fallbacks beside renamed `meridian_*` metrics for one
retention window so a rolling rename does not create an observability blackout.
The readiness endpoint requires Postgres, a pooled Redis checkout, and all

View file

@ -27,7 +27,7 @@ spec:
> on (namespace) sum by (namespace) (max by (namespace, pod) (up{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == bool 1))
or (max by (namespace, pod) (up{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == 1)
unless on (namespace, pod) max by (namespace, pod) (meridian_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}
or buzz_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}})))
or codebase_chat_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}})))
for: 5m
labels: { severity: page, owner: meridian-relay }
annotations:
@ -43,7 +43,7 @@ spec:
description: Prometheus has no kube-state-metrics replica inventory for the relay deployment, so a completely undiscovered replica cannot be distinguished from healthy capacity.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianRelayNotReady
expr: max by (namespace, pod) (meridian_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == 0 or buzz_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == 0) > 0
expr: max by (namespace, pod) (meridian_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == 0 or codebase_chat_relay_ready{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} == 0) > 0
for: 2m
labels: { severity: page, owner: meridian-relay }
annotations:
@ -69,7 +69,7 @@ spec:
- name: meridian-relay.dependencies
rules:
- alert: MeridianDatabasePoolSaturated
expr: (meridian_db_pool_active{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} / clamp_min(meridian_db_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}, 1) or buzz_db_pool_active{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} / clamp_min(buzz_db_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}, 1)) > 0.9
expr: (meridian_db_pool_active{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} / clamp_min(meridian_db_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}, 1) or codebase_chat_db_pool_active{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} / clamp_min(codebase_chat_db_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}, 1)) > 0.9
for: 10m
labels: { severity: warning, owner: meridian-relay }
annotations:
@ -77,7 +77,7 @@ spec:
description: More than 90 percent of writer connections have been active for ten minutes; inspect slow queries before raising the pool ceiling.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianReplicaFenceClosed
expr: max by (namespace, pod) (meridian_db_replica_fence_open{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or buzz_db_replica_fence_open{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) == 0 and on (namespace, pod) max by (namespace, pod) (meridian_db_read_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or buzz_db_read_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) > 0
expr: max by (namespace, pod) (meridian_db_replica_fence_open{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or codebase_chat_db_replica_fence_open{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) == 0 and on (namespace, pod) max by (namespace, pod) (meridian_db_read_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or codebase_chat_db_read_pool_max{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) > 0
for: 5m
labels: { severity: warning, owner: meridian-relay }
annotations:
@ -85,7 +85,7 @@ spec:
description: Replica reads have remained unsafe for five minutes and traffic is pinned to the writer.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianStorageSweepFailed
expr: max by (namespace, pod) (meridian_storage_sweep_ok{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or buzz_storage_sweep_ok{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) == 0
expr: max by (namespace, pod) (meridian_storage_sweep_ok{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}} or codebase_chat_storage_sweep_ok{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}) == 0
for: 20m
labels: { severity: warning, owner: meridian-relay }
annotations:
@ -95,7 +95,7 @@ spec:
- name: meridian-relay.correctness
rules:
- alert: MeridianAuditWriteErrors
expr: increase(meridian_audit_log_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(meridian_audit_send_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(buzz_audit_log_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(buzz_audit_send_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0
expr: increase(meridian_audit_log_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(meridian_audit_send_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(codebase_chat_audit_log_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(codebase_chat_audit_send_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0
for: 0m
labels: { severity: page, owner: meridian-relay }
annotations:
@ -103,7 +103,7 @@ spec:
description: Any audit enqueue or durable-chain write error is a correctness incident and pages immediately.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianPostCommitDispatchErrors
expr: increase(meridian_post_commit_dispatch_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(buzz_post_commit_dispatch_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0
expr: increase(meridian_post_commit_dispatch_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0 or increase(codebase_chat_post_commit_dispatch_errors_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 0
for: 0m
labels: { severity: page, owner: meridian-relay }
annotations:
@ -111,7 +111,7 @@ spec:
description: A durable event was accepted but at least one fan-out or side-effect stage failed.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianWorkflowFailures
expr: sum(increase(meridian_workflow_runs_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }},status="failed"}[15m])) > 5 or sum(increase(buzz_workflow_runs_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }},status="failed"}[15m])) > 5
expr: sum(increase(meridian_workflow_runs_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }},status="failed"}[15m])) > 5 or sum(increase(codebase_chat_workflow_runs_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }},status="failed"}[15m])) > 5
for: 5m
labels: { severity: warning, owner: meridian-relay }
annotations:
@ -119,7 +119,7 @@ spec:
description: More than five workflow failures in fifteen minutes suggests a dependency, definition, or secret-resolution regression.
runbook_url: {{ .Values.prometheusRule.runbookUrl | quote }}
- alert: MeridianWebSocketBackpressureDisconnects
expr: increase(meridian_ws_backpressure_disconnects_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 25 or increase(buzz_ws_backpressure_disconnects_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 25
expr: increase(meridian_ws_backpressure_disconnects_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 25 or increase(codebase_chat_ws_backpressure_disconnects_total{namespace={{ $namespace | quote }},pod=~{{ $podPattern | quote }}}[10m]) > 25
for: 5m
labels: { severity: warning, owner: meridian-relay }
annotations:

View file

@ -56,7 +56,7 @@ PROMETHEUS_PORT=9090
# _sqlx_migrations histories and corrupts both.
MERIDIAN_CONTROL_DB=meridian_control_plane
MERIDIAN_CONTROL_PUBLIC_ORIGIN=https://register.meridian.r2d2.office.ilab.zone
MERIDIAN_CONTROL_API_BASE_PATH=/api/codebase
MERIDIAN_CONTROL_API_BASE_PATH=/api/meridian
MERIDIAN_CONTROL_COMMUNITY_HOST_SUFFIX=communities.meridian.r2d2.office.ilab.zone
MERIDIAN_CONTROL_RELAY_SCHEME=wss
MERIDIAN_CONTROL_COMMUNITY_LIMIT=3
@ -84,7 +84,7 @@ MERIDIAN_CONTROL_IDENTITY_PROVIDER=oidc
MERIDIAN_RELAY_HOST=relay.meridian.r2d2.office.ilab.zone
MERIDIAN_CONTROL_HOST=register.meridian.r2d2.office.ilab.zone
MERIDIAN_COMMUNITY_HOST_SUFFIX=communities.meridian.r2d2.office.ilab.zone
MERIDIAN_COMMUNITY_HOST_SUFFIX_RE=communities\.codebase\.design
MERIDIAN_COMMUNITY_HOST_SUFFIX_RE=communities\.meridian\.r2d2\.office\.ilab\.zone
# Must be a DNS-01 resolver: community hosts are minted under a wildcard, and
# Let's Encrypt will not issue *.communities.… over HTTP-01.

View file

@ -38,24 +38,20 @@ CTX="$(kubectl config current-context)"
log "context: $CTX"; kubectl get nodes | tee "$EVID/nodes.txt"
# ── 1. corporate-proxy CA + npm mirror (TLS-intercepting gateway) ────────────
# Two stacked blocks on Block's network: (a) the gateway re-signs TLS with
# internal CAs the build container doesn't trust; (b) public registry.npmjs.org
# is policy-blocked (Dependency Confusion mitigation), so npm/corepack must use
# the Artifactory mirror. Both no-op on a normal network (build-args stay unset).
# Two independent escape hatches for a restricted corporate network: (a) a
# TLS-intercepting gateway re-signs certificates with an internal CA the build
# container does not trust; (b) the public npm registry is policy-blocked, so
# npm/corepack must use an internal mirror. Both no-op on a normal network —
# the build-args stay unset and nothing below changes.
CA_ARG=()
REG_ARG=()
# (a) Build a complete internal-CA bundle from the macOS System keychain.
if [ ! -f "$CA_PEM" ] && command -v security >/dev/null 2>&1; then
log "exporting Block internal CA bundle from System keychain"
: > "$CA_PEM"
for name in "Cloudflare Gateway CA" \
"Service To Service AWS Native CA production G0" \
"Corp Systems AWS Native CA production G0" \
"Block, Inc CA G1" \
"Square Primary Certificate Authority - G2"; do
security find-certificate -a -c "$name" -p /Library/Keychains/System.keychain >> "$CA_PEM" 2>/dev/null || true
done
fi
# (a) Supply your own CA bundle if your network intercepts TLS: concatenate the
# PEM-encoded internal CAs into `deploy/local/proxy-ca.pem` and re-run. On
# macOS that is roughly:
# security find-certificate -a -c "<CA name>" -p /Library/Keychains/System.keychain \
# >> deploy/local/proxy-ca.pem
# (The previous automatic export was keyed to Block's corporate CA names and
# does not apply to a self-hosted deployment.)
if [ -f "$CA_PEM" ] && grep -q 'BEGIN CERTIFICATE' "$CA_PEM"; then
CA_ARG=(--build-arg "EXTRA_CA_CERTS=deploy/local/proxy-ca.pem")
log "using proxy CA bundle ($(grep -c 'BEGIN CERTIFICATE' "$CA_PEM") certs)"

View file

@ -349,14 +349,14 @@ function maybeEnableMeridianTranslucent(
* continuation can't re-enable translucency after a newer theme superseded it.
*/
async function applyMeridianVibrancy(themeName: string) {
const codebaseChat = isMeridianTheme(themeName);
const meridianTheme = isMeridianTheme(themeName);
const requestToken = ++meridianVibrancyRequest;
// Meridian Light and Meridian Dark use the same native material. Rebuilding the
// NSVisualEffectView on every mode change briefly clears the layer behind
// the webview and makes the new CSS theme appear late. Keep the installed
// layer and let applyTheme swap only the color tokens.
if (codebaseChat && meridianVibrancyEnabled && meridianVibrancyReady) {
if (meridianTheme && meridianVibrancyEnabled && meridianVibrancyReady) {
maybeEnableMeridianTranslucent(themeName, requestToken);
return;
}
@ -374,18 +374,18 @@ async function applyMeridianVibrancy(themeName: string) {
try {
await invokeTauri<void>("set_window_vibrancy", {
enabled: codebaseChat,
enabled: meridianTheme,
material: MERIDIAN_VIBRANCY_MATERIAL,
});
// A newer theme change superseded this request while the IPC was in flight;
// that later call owns the current translucency state, so don't clobber it.
if (requestToken !== meridianVibrancyRequest) return;
meridianVibrancyEnabled = codebaseChat;
meridianVibrancyEnabled = meridianTheme;
// Native layer is installed. Record readiness and try to enable translucency
// — but only if `applyMeridianSidebar` has already installed the Meridian gradient
// vars. If that effect hasn't landed yet (the IPC won the race), it will
// call maybeEnableMeridianTranslucent itself once the marker is applied.
if (codebaseChat && isMacPlatform()) {
if (meridianTheme && isMacPlatform()) {
meridianVibrancyReady = true;
maybeEnableMeridianTranslucent(themeName, requestToken);
}

View file

@ -28,7 +28,7 @@
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.meridian.deeplink</string>
<string>zone.ilab.office.r2d2.meridian.deeplink</string>
<key>CFBundleURLSchemes</key>
<array>
<string>meridian</string>

View file

@ -11,11 +11,11 @@ void main() {
});
test('borrow the GitHub palettes', () {
final codebaseChat = findTheme(meridianThemeName)!;
final meridianLight = findTheme(meridianThemeName)!;
final github = findTheme('github-light')!;
expect(codebaseChat.bg, github.bg);
expect(codebaseChat.fg, github.fg);
expect(codebaseChat.comment, github.comment);
expect(meridianLight.bg, github.bg);
expect(meridianLight.fg, github.fg);
expect(meridianLight.comment, github.comment);
final meridianDark = findTheme(meridianDarkThemeName)!;
final githubDark = findTheme('github-dark')!;