{"slug": "trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production", "title": "trelix v3.2.2 to v3.3.8: A GitHub App Already Hardened and Running in Production, and a Connector That Never Touches a Pixel", "summary": "A developer documented thirteen tagged releases of the Trelix project from v3.2.2 through v3.3.8, after discovering that the published v3.2.1 Docker image never contained the trelix-mcp entrypoint and that its console script silently ignored all flags. The response included a real-subprocess E2E suite in CI that installs published artifacts and spawns trelix-mcp over stdio JSON-RPC, plus a 742-line verify_release.py script with five independent check functions. The developer notes that while the code itself held up under source review, the surrounding documentation and changelog repeatedly drifted from what the code actually does.", "body_md": "`docker run --entrypoint trelix-mcp ghcr.io/sairam0424/trelix:3.2.1 --help` returns exit code 127: command not found. Not a typo, not a stale tag — the published 3.2.1 image never contained `trelix-mcp` at all. The builder stage copied core `trelix` and nothing else, and `.dockerignore`'s blanket `packages/` exclusion would have blocked the MCP package even if someone had remembered the `COPY` line. A second, unrelated defect shipped in the same release: the published `trelix-mcp` console script ignored every flag you gave it — `--help`, `-h`, `--version`, anything — and silently launched the real MCP stdio server instead. Both were found the only way they could have been found: by installing the actual published artifacts and running them, not by reading a source tree that had never been wrong.\n\nThat's v3.2.2, and it's where this span starts. This article covers the thirteen tagged releases from v3.2.2 through v3.3.8 — 2026-08-26 through 2026-09-25 — picking up the day after the last article in this series ended at v3.2.1. v3.3.8 is the current release: tagged, pushed, marked \"Latest,\" and — I checked this directly rather than trusting the changelog — live on PyPI right now.\n\nThe prior span's throughline was a green test suite that never touched the bug it claimed to cover — a `MagicMock` standing in for an embedder, an all-ones attention mask making masked and unmasked math identical, a unit test that asserted a bug as its own specification. That defect class is still present in this span (more on it below), but it's no longer the dominant story. Two things sit ahead of it now.\n\nThe first is that the response to catching defects graduated from \"write a regression test\" to \"build a system that makes this class of defect structurally unable to reach a tag again.\" The second is quieter and, I think, more interesting: almost everything genuinely new in this span — a diagram connector, an MCP protocol upgrade, a retriever-caching fix, a compression provider, an audit-log pruning mechanism — turned out to be sound at the code level when I went and checked it against source. What kept drifting was the layer just above the code: a renamed class the CHANGELOG never caught up to, a performance number with no artifact behind it, a guarantee that overstates what the code actually does. If the last article's angle was \"the code lied to the tests,\" this one's second angle is closer to \"the code is right, and the notes about the code keep being slightly wrong\" — which is a more mature failure mode, and one that only shows up once the cruder failure mode has already been closed off.\n\nv3.2.2's fixes were correct but ordinary. What matters is what happened next. v3.2.3, two releases later, turned the exact manual \"install the real published artifacts and run them\" pass that had just caught two live defects into permanent CI: a real-subprocess E2E suite that spawns `trelix-mcp` as an actual OS process talking real stdio JSON-RPC, fresh-venv installs of all four published packages, a `smoke-test-built-artifacts` job that `release.yml`'s `publish` job is now hard-gated on, a Docker check that `trelix-mcp` is actually present in the built image, and a Helm-lint check that the rendered image tag matches `Chart.yaml`. The same release fixed a real, separate bug along the way — a `LIKE`-wildcard escaping gap in `path_filter`-scoped BM25/grep queries, where unescaped `_` and `%` in ordinary directory names were being read as SQL wildcards. Not injection (the queries were already parameterized), just a distinct semantics gap, closed with a shared `escape_like_pattern()` helper and an `ESCAPE '\\'` clause.\n\nThe centerpiece is `scripts/verify_release.py`, a 742-line script with five independent check functions that each return a list of failures and never raise — so one category's bug can't hide another's results. `check_pypi_installs` does a fresh venv per package and a real `pip install <pkg>==<version>`, then a real console-script and import smoke test against the version string. `check_docker_images` pulls both the plain and `-local` tags and runs the exact `--entrypoint trelix-mcp --version` command that returned 127 on the unpatched 3.2.1 image — this check exists specifically because that command failed once, silently, in production. `check_helm_chart` checks out `v<version>` into a throwaway `git worktree`, never the caller's own working tree, and runs `helm lint`/` helm template` across all three store backends. `check_github_release_binaries` downloads and `file(1)`-checks all four platform assets and executes whichever one matches the current host, printing rather than silently skipping when it can't run a foreign-arch binary. `check_security_audit` runs `pip-audit --format json` on all four packages in a fresh venv and separately scans every wheel for `.env*`, `.git`-path, or `credential`/` secret`-named files that should never ship.\n\nv3.2.4 wired this permanently into CI via `.github/workflows/verify-release.yml`: it waits for both `release.yml` and `docker-publish.yml` to go green for a pushed tag, then runs `verify_release.py` automatically and posts a PASS/FAIL comment, with the manual command still available for ad-hoc re-runs. I read the 99-line workflow directly — it uses `env:` passthrough rather than `${{ }}` shell interpolation, matching `docker-publish.yml`'s own injection-avoidance convention. v3.2.5, the same day, fixed a smaller instance of the identical class of gap: the frozen PyInstaller binary's embedder error message told users to `pip install 'trelix[local]'`, which does nothing at all for a binary that never touches the host's Python or pip.\n\nI ran the one check in this whole dossier that didn't rely on reading source: `curl https://pypi.org/pypi/trelix/json`. `info.version` is `3.3.8`. The release history includes 3.2.2 through 3.2.5, then 3.3.0, 3.3.5, 3.3.6, 3.3.7, 3.3.8 — 3.3.1 through 3.3.4 are conspicuously absent from PyPI's own list, which I take to mean they were likely never tagged as standalone PyPI releases rather than any kind of cover-up; I didn't dig further and I'm flagging it rather than resolving it. `trelix-mcp` is confirmed in lockstep at 3.3.8 too, wheel and sdist both uploaded 2026-09-25T09:54 UTC. That's a directly executed, non-paraphrased result — the single strongest claim in this whole article, because it's the one number here that isn't source-reading, it's a live check against the real index.\n\nEvery other release in this span is a patch. v3.3.0 is a deliberate minor bump with four real breaking changes, which is the honest way to signal \"this will require you to change something,\" and I want to give it credit for that framing even where its own migration doc undersells it.\n\nThe four changes: the Python floor moved to `>=3.12` across all three packages; the `TRELIX_RETRIEVAL_FLARE_MAX_ITER` env alias was removed outright, with zero remaining references anywhere outside docs; `AWS_REGION` is now hard-required for the Bedrock backend, raising `ValueError` where it used to silently default to `us-east-1`; and the Anthropic backend now silently no-ops on a `temperature` argument instead of forwarding it, logging a one-time warning instead. That last pair — a required env var and a silently dropped parameter — are exactly the two changes most likely to actually break a deployment on upgrade, and they are, by my own read of `docs/migration/v3.2-to-v3.3.md`, the two the migration guide doesn't mention at all. The doc covers three of the six real changes and its own closing section states the LLM-provider and MCP changes are \"not yet released\" — they shipped the same day as the doc, under the same version tag. If you're upgrading past v3.3.0, read the CHANGELOG, not the migration guide.\n\nThe release also introduces a reasoning-content abstraction across both cloud backends — and here's a naming error worth stating plainly because it'll cost you time if you don't know about it: the CHANGELOG calls this type `ReasoningBlock`. The actual class, which I confirmed directly in `src/trelix/llm/client.py` (line 29), is named `ThinkingBlock`. A repo-wide grep for `ReasoningBlock` across `src/`, `packages/`, and `tests/` returns zero matches. `ChatResponse.thinking_blocks: list[ThinkingBlock]` plus a back-compat `ChatResponse.thinking: str | None` is what actually ships, and both the Anthropic and Bedrock backends build `ThinkingBlock` instances correctly, including the previously-dropped `redacted_thinking` variant. If you grep the codebase for the CHANGELOG's own name, you'll find nothing — use `ThinkingBlock`.\n\nSeparately, MCP gained SEP-2322 support: `AgentResult.needs_input`, set only on an explicit `CLARIFY` action from the agent loop, lets the `ask_agent` MCP tool wrap an `ElicitRequest` inside an `InputRequiredResult` when the agent genuinely needs clarification, resuming later via a dedicated answer-extraction path. Non-elicitation callers are unaffected — it's additive. This is the most solidly verified claim in the release, and also the one place the CHANGELOG's own claim doesn't hold all the way through: Bedrock's `stream()` method got a fix for dropped reasoning deltas, and the changelog describes it as fixing dropped reasoning *and* tool-use deltas together — \"now logged at debug level instead of vanishing untraced.\" Reading the actual diff, only the `reasoningContent` half shipped; there is no branch at all for `toolUse` deltas, and a `contentBlockDelta` carrying incremental tool-call argument fragments still falls through with zero logging, exactly as it did before the fix. It's a small gap, but it's the kind of gap that only shows up when you read the code instead of the note describing it.\n\nThe prior article's four-instance spine — a green suite that never exercised the defect — recurs here too, but this time it's the product of a deliberate audit rather than an accident someone happened to trip over: a correctness sweep hunting one specific bug pattern, qualified-name collisions, that pulled 19 independently confirmed defects across seven subsystems in v3.2.4 alone.\n\nTwo of them are worth walking through because they're the same shape at different layers. The installed tree-sitter-java grammar emits `formal_parameters`/` formal_parameter` for a Java record's component list, not the `record_parameters`/` record_component` node types the extractor had assumed — every record's component list had been rendering empty. The fix shares a single `_extract_type_list_edges()` helper across six call sites rather than patching the one spot that got noticed. Rust had the mirror problem: `use foo::{Alpha, Beta}` — the dominant real-world Rust import form — fell through to a generic single-name fallback because the code checked for `use_tree_list`/` use_tree` node types tree-sitter-rust doesn't actually emit; the real types are `scoped_use_list`/` use_list`. Pre-fix, that path would have produced the literal string `\"{Alpha, Beta}\"` as a bogus import name.\n\nOne layer up, in the federation retriever, `make_scip_symbol_id()` hashed only `(package, version, qualified_name)` — so two files sharing a qualified name, like two `def main()` entry points in different modules, collided, and the second was silently dropped via `INSERT OR IGNORE`. The fix folds `file_path` into the hash. And a fix in v3.3.4's chunker — where oversized symbols had their excess body silently discarded and replaced with a truncation marker, unrecoverable by retrieval — created a *new* collision surface one release later: multiple chunks could now share one `symbol_id`, and the fusion/dedup layer's key didn't yet account for that. v3.3.5's own fix docstring is candid about this being the same defect class recurring one abstraction level down from a prior bug, in code the same engineers had just touched days earlier. That's the honest version of \"we fixed it\" — not zero recurrence, but recognizing the recurrence immediately and naming it as such rather than treating each instance as a surprise.\n\nA separate 94-agent chunking-strategy study, read in full, found trelix's one-symbol-per-chunk strategy underperforming alternative strategies by 3.57–5.64 percentage points of exact-match accuracy against a real published benchmark — and then rated its own recommendation \"LOW-MEDIUM confidence a full redesign is worth building right now,\" because a redesign would break the 1-chunk-to-1-symbol cardinality assumption baked into the reranker and graph-context code — \"the exact class of bug this session already found three separate times in adjacent code this same week.\" That's a research doc citing its own team's bug history as the reason to be cautious about its own conclusion, which is a genuinely unusual thing to see in a changelog-adjacent artifact, and I think it's a better outcome than either shipping the redesign on a hunch or ignoring the study.\n\nThere are two real, shipped ways to get trelix's review on a PR, and both post a check literally named \"trelix Code Review.\" The first is a GitHub Actions workflow that trusts nothing beyond the repo's own `GITHUB_TOKEN`. The second is a standalone GitHub App — TypeScript/Express, webhook-driven, zero workflow YAML required in the installing repo, confirmed live right now on Railway (production environment, deployment from commit `ac94e1c` on `main`, instance status RUNNING). The tradeoff between the two is exactly what it looks like: the Actions path trusts nobody, the App path is more convenient but grants a real third party real permissions on your repo.\n\nIt is not, and should not be described as, a Marketplace app. `infra/github-app/README.md` says this outright, in words I'm quoting rather than paraphrasing: \"Not claimed: GitHub Marketplace listing. This App is installable and hardened, not Marketplace-verified.\" `docs/ROADMAP.md` lists the Marketplace listing as an explicit open backlog item, gated on needing roughly 100 installations before GitHub will even review it for the listing — an adoption gate, not an engineering one.\n\nThe security posture underneath, which I read at the file level rather than taking on faith: webhook signature verification uses `timingSafeEqual` via `@octokit/webhooks-methods` and rejects unauthorized requests before the payload body is even read; installation tokens are minted and cached per-config in a `WeakMap`, with JWT-mode auth correctly *not* cached; PR checkouts clone into a temp workspace and fetch `refs/pull/<n>/head`, which is the correct ref for fork PRs, with the token passed only via a `GIT_ASKPASS` environment variable — never in a URL, argv, or git config, where it could leak into logs or process listings; and there's a 25MB payload cap applied *before* signature verification, which is the only defense against an oversized unsigned body. As of v3.3.8, a review that times out or crashes now always posts a completed Check run instead of leaving the PR with no status at all — `timed_out` conclusion when the process was SIGTERM'd, `neutral` otherwise. That's a real, shipped fix for a real silence-on-failure gap, and it's a good instinct for a system whose entire value proposition is a status check people trust.\n\nWhat's still open, and what I want to be careful not to resolve in either direction just because it would make a tidier ending: there's a documented, not-yet-confirmed-resolved concern that the App's own review of trelix's own large codebase has been getting SIGKILLed before indexing finishes, suspected to trace back to `review-runner.ts`'s hardcoded 5-minute `INDEX_TIMEOUT_MS`/` REVIEW_TIMEOUT_MS`. I checked those constants directly — they're unchanged at 5 minutes, in this span and since. But I also checked every CHANGELOG line, every ROADMAP line, and the git log for any commit connecting those two constants to a SIGKILL-during-self-review incident, and found nothing. The only `SIGKILL` mentions anywhere in the App's code concern a completely different scenario — OOM-killed processes leaving stale temp workspaces behind, which a `sweepStaleWorkspaces()` cleanup at boot handles. So: the timeout constants are exactly as short as the concern says, but nothing in the repository substantiates that they're actually the cause of a self-review failure, or that the failure is still happening at all. I'm stating this as an open question because it is one, not because I want to end this section on a cliffhanger. It should not be confused with a different, already-closed issue in the same App: a missing `TRELIX_EMBEDDER_PROVIDER=azure` env var on Railway, which caused indexing to silently fall back to a never-installed local embedder and fail — that was fixed on 2026-09-22 via a deployment config change, not a code change, and it's fully resolved. These are two unrelated bugs at two unrelated layers; only one of them is closed.\n\nv3.3.6 shipped a fifth connector alongside Jira, TestRail, Xray, and Linear: `trelix connector sync <repo> diagram`, for local `.drawio` (diagrams.net) files. The mechanism is more mundane than it sounds, in the best way — every shape in a drawio file's mxGraph XML carries its label in a plain `value=\"...\"` attribute, and every edge is an explicit `<mxCell edge=\"1\" source=... target=...>`, so the whole diagram is fully text-describable without any vision model. The connector truncates the XML to 20,000 characters and sends it through the same text-only chat client every other LLM call site in trelix already uses. Raster images — PNG, JPG — are explicitly out of scope; there's no glob or handling for them anywhere.\n\nv3.3.7 fixed a real bug in the fallback path: when captioning failed, the connector used to discard the diagram's real content entirely and report just a character count. Now it regex-extracts actual `mxCell value=\"...\"` shape labels, HTML-unescapes before stripping inline tags (the order matters, since drawio escapes attribute values, so entities have to decode before tags can be safely stripped), de-duplicates in first-seen order, caps at 50 labels, and only falls back to the old length-only message when literally zero labels exist. It's a small fix but a real one — the difference between \"the diagram failed to caption, here's 4,200 characters\" and \"the diagram failed to caption, here are the 12 real shape labels it contains\" is the difference between a useless fallback and a usable one. There's no diagram-specific config or env var; it rides whichever LLM provider is already configured, and the artifact linker treats a diagram exactly like a Jira ticket — same edge shape, same matching logic, no special-cased code path anywhere.\n\nv3.3.8 fixed a real inefficiency: `trelix-mcp`, the LangChain retriever, and the LlamaIndex retriever all used to rebuild a fresh `Retriever` object on every single call. They're cached now — module-level with a lock in the MCP server, invalidated correctly on reindex, per-instance in both framework adapters. The mechanism is sound and I'd recommend the fix without reservation. What I can't recommend repeating is the specific number attached to it: the CHANGELOG cites a \"24.5x–52.7x measured speedup,\" and that figure appears nowhere except the CHANGELOG's own prose — not in the commit message I pulled directly, not in the PR, not in any test. The commit itself says only that three consecutive calls in one session took 13-16 seconds, then 5, then 5, with zero warm-up benefit — no multiplier, and no recorded post-fix warm baseline anywhere to derive one from. The fix is real; the multiplier isn't backed by anything outside the CHANGELOG's own unsupported prose, so I'm not repeating it as measured.\n\nThe rest of this theme holds up better. `trelix-mcp`'s and the graph community-detection code's use of `nx.pagerank` now catches `ImportError` and degrades to a uniform-score fallback instead of crashing, because scipy is only imported lazily on first call and the frozen binary deliberately excludes it — a real crash class closed with two small, defensive catches. Qdrant gained int8/binary quantization support, with one small flag: the commit claims verification against `qdrant-client==1.19.0`, but the lockfile currently pins 1.18.0. Not a contradiction — a dev-venv version never captured in the lockfile is the more likely explanation than anything adversarial — but not independently confirmable either, so treat \"verified against 1.19.0\" as unconfirmed rather than false. A direct Cohere embedder landed using `cohere.ClientV2` rather than routing through Bedrock, with a 96-item batch limit. And an abstractive compression provider shipped with a real accuracy gap between what the CHANGELOG claims and what the code guarantees: the CHANGELOG says the signature *and* docstring line are \"always kept byte-for-byte.\" The actual `_must_keep()` logic only locates and preserves the declaration line itself — the docstring on subsequent lines is forwarded to the LLM as context, with no verbatim guarantee, and can come back paraphrased. The test suite's own docstring gets this right where the CHANGELOG doesn't: \"the declaration line is ALWAYS kept verbatim,\" singular. If you're relying on this feature to preserve docstrings exactly, don't — that's what the separate extractive compressor genuinely does.\n\nThe litellm floor moved to `>=1.90.2`, closing two CVEs the project's own pins cite as CVSS 9.8 — a Host-header authentication bypass and an unauthenticated SSTI leading to RCE — plus a third SSRF issue via nested `user_config`. I can confirm these citations are in the repo's own dependency comments; I could not independently cross-check the CVE IDs against GitHub's live advisory database from this sandbox, so I'm attributing the \"two CRITICAL, CVSS 9.8\" framing to trelix's own release notes rather than an outside source I verified myself. Separately, `pydantic-settings` CVE-2026-58203 was patched by a floor bump, and I confirmed the vulnerable feature — `secrets_nested_subdir` — was never actually called anywhere in the codebase, so this one was zero real exposure even before the bump; a clean case of patching a dependency that was never actually exploitable through this code.\n\nThe most substantial security addition is `trelix audit prune` — a real hash-chain watermarking implementation for pruning old audit-log rows without breaking chain verification, with an `--dry-run` mode that's confirmed not wired into any in-process scheduler. What struck me reading it is that the module's own docstring is more conservative than the CHANGELOG's framing: it states plainly that this is \"tamper-evident, not tamper-proof,\" and it enumerates exactly which tamper shapes remain undetectable — a total wipe that also clears SQLite's own internal sequence table, for instance. A security feature whose own documentation undersells its guarantees relative to the marketing copy around it is the rare direction I'm happy to see the drift run in.\n\nTwo releases of CLI/API/extension hardening are worth a fast pass. v3.3.1 fixed four real VS Code extension bugs: a JSON.parse crash whenever the MCP client returned a plain-text error instead of JSON, a path escape hatch for GUI-launched editors whose PATH excludes pip/uv, inline symbol links in chat responses, and a CodeLens replacing a global QuickPick for blast-radius visualization. v3.3.7 fixed a `.vscodeignore` gap that had been shipping a local vector database file and gigabyte-plus of sourcemaps inside the packaged extension — `vsce` ignores `.gitignore` entirely once `.vscodeignore` exists, so the two files having drifted apart had real consequences. The same release moved four CLI/REST failure modes from raw tracebacks or bare 500s to labeled, structured errors: `eval`/` eval-synthesis`/` migrate-vectors` now print one-line labeled errors instead of stack traces; a missing golden file for synthesis eval now raises `FileNotFoundError` instead of silently returning zero-filled metrics; the REST API's `DimensionMismatchError` and `ImportError` now return structured JSON 500s instead of Starlette's plain-text default; and static-token auth now checks `Authorization: Bearer` independently of `X-Trelix-Api-Key`, via `hmac.compare_digest`, closing a path where the Bearer header was silently ignored.\n\nI want to name the pattern across this article's corrections rather than let them read as scattered nitpicks, because I think they're one thing: `ReasoningBlock` vs. `ThinkingBlock`, the half-shipped `toolUse`-delta logging claim, the unaudited 24.5x-52.7x figure, the docstring-verbatim overstatement, a migration guide that's stale relative to its own release date, a `docs/WHY_TRELIX.md` version-scope stamp that still reads v3.3.7 while the package is at v3.3.8 — none of these are code bugs. Every one of them is the layer just above the code: the sentence describing what the code does, written slightly faster or slightly more optimistically than the code itself justifies. That's a different failure mode than a `MagicMock` hiding a broken embedder, and it's arguably a sign of health rather than decline — it only becomes the dominant story once the release-verification discipline from earlier in this span has already closed off the cruder failure mode of a defect reaching a tag at all. It's also, not coincidentally, exactly the discipline this article itself needed applied to it: this dossier exists because the same standard — check the sentence against the source, not against the intent — got turned on the release notes themselves, and turned up a correction on nearly every theme it touched.\n\nIf you're upgrading from anywhere before v3.3.0, read the CHANGELOG for that release specifically rather than the migration guide — the guide is missing the `AWS_REGION` requirement and the silently-dropped `temperature` parameter, which are the two changes most likely to actually break your deployment. If you're grepping the codebase for the reasoning-content type, look for `ThinkingBlock`, not `ReasoningBlock`. If you're evaluating the retriever-caching fix, trust the mechanism and not the specific multiplier. If you're relying on abstractive compression to preserve a function's docstring exactly, it doesn't — only the declaration line is guaranteed verbatim. And if you're deciding whether to install the GitHub App or rely on the Actions workflow for PR review, both post the same \"trelix Code Review\" check, but only one of them asks you to trust a third party — and if you run the App against a codebase the size of trelix's own, know that a timeout-related self-review concern from a week ago hasn't been confirmed fixed, or confirmed to still be happening either.\n\nNone of that is a reason not to upgrade. The live PyPI check I ran directly — not read, not paraphrased, executed — confirms v3.3.8 across all four packages, published in lockstep on 2026-09-25. The release-verification infrastructure built in v3.2.3 and v3.2.4 means that check itself, along with a Docker `--version` smoke test and a Helm chart lint, now runs automatically against every tag before it's allowed to publish, which is a meaningfully different guarantee than the source tree alone ever gave you. What changed across these thirteen releases isn't that trelix stopped shipping defects — the parser sweep alone found 19 of them in one release. What changed is that the mechanism for catching what a green test suite misses stopped being a person doing it once, by hand, out of frustration, and started being a system that does it on every tag, whether anyone remembers to ask or not. The part that hasn't caught up yet is the sentence describing the fix, which is a smaller problem to have than the one this project started with nine weeks ago, and a more honest place to end an audit than pretending it isn't there at all.", "url": "https://wpnews.pro/news/trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production", "canonical_source": "https://dev.to/sai_ram_0000/trelix-v322-to-v338-a-github-app-already-hardened-and-running-in-production-and-a-connector-3hng", "published_at": "2026-09-26 11:08:08+00:00", "updated_at": "2026-09-26 11:30:10.481045+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "mlops", "ai-agents"], "entities": ["Trelix", "PyPI", "GitHub", "Docker", "Helm"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production", "markdown": "https://wpnews.pro/news/trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production.md", "text": "https://wpnews.pro/news/trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production.txt", "jsonld": "https://wpnews.pro/news/trelix-v3-2-2-to-v3-3-8-a-github-app-already-hardened-and-running-in-production.jsonld"}}