Identity#
Gillish Node is a WordPress plugin by the Gillish studio. It combines link management (cloaked redirects, click tracking, affiliate cloaking, A/B testing, auto-linking) with content-structure visualization (an interactive D3.js graph and a pluggable SEO-signal pipeline) and a Google + Rank Tracker system on top.
- Entry file:
gillish-node.php· namespace:Gillish\Node\*(PSR-4, strict types) · asset prefix:gn- - CPT:
gillish_node· shortcode:[node]· default URL prefix:/node/(configurable) - Requires WP: 6.0+ · Requires PHP: 8.2 (8.4 recommended, enables the native
Dom\HTMLDocumentparser in the auto-link walker) - Architecture: modular, 15 feature modules under
src/Modules/plus the Content Graph undersrc/Graph/; 13 modules individually toggleable viaModuleRegistry, the rest infrastructure-and-always-on - 7 custom DB tables (click log, two auto-link tables, three Google/GSC caches, rank pins), schema-versioned
- Frontend footprint: zero JS/CSS on public pages by default, everything renders server-side via the
the_contentfilter; redirects run before any template loads - Code quality: 0 errors in Plugin Check held as a release-gate invariant; PHPStan level 9; every hook and option key a typed constant
- Sibling product: Gillish Cairnstone (visual block builder, namespace
Gillish\Cairnstone), standalone, but link-aware when Node is also active
The pitch: three systems#
One plugin, three systems that share a CPT, an admin shell, and a brand:
- Link Management: managed redirects as WordPress posts, click analytics, affiliate cloaking, A/B routing, dynamic per-link rules, and a keyword auto-linker.
- Content Graph: an interactive D3.js map of the site’s internal-link architecture with a 5-level topical classification, Link Gravity authority scoring, and 14 pluggable SEO-signal detectors.
- Google + Rank Tracker: service-account GSC + GA4 sync, a 16-month historical backfill, a Search Performance page, and a passive (no-SERP-API) Rank Tracker built entirely on cached GSC data.
No direct competitor combines link management, auto-linking, Content Graph visualization, GA4/GSC integration, and modular SEO-signal detection in a single plugin. That overlap is the defensible position.
Link management core#
- Cloaked redirect URLs with a configurable prefix; 301 / 302 / 307 per link
- Tri-state
relresolution, nofollow / sponsored / ugc / noreferrer / open-new-tab resolved per-link → global default → hardcoded fallback - Three insertion modes, Synced (attributes resolved from the DB at render time, so one settings change updates every placement instantly), Direct (attributes baked into the HTML, opt-out via
class="gn-direct"), and Shortcode ([node id="42"]/[node slug="…"]) - Link scheduling (start, expiry, expiry action), max-clicks cap, parameter forwarding, sub-ID path tracking with named labels, per-link CSS classes and preferred anchor text
- Link categories/groups, uncloaked direct-to-destination mode
Click tracking & privacy#
- Async click logging via
wp_schedule_single_event, zero redirect latency - SHA-256-hashed IP salted with
wp_salt('auth')(one-way); per-field privacy toggles for IP hash, user agent, referer, country, sub-ID, variant, bot flag - Configurable retention (30 / 90 / 180 / 365 days or forever) with monthly aggregation before deletion so totals are never lost
- Bot filtering on logging via a ~1500-pattern bundle, bot clicks stored flagged and filterable out of every analytics query
- WordPress Privacy API exporter + eraser (GDPR)
Redirect routing & dynamic rules#
Each managed link can carry an ordered, first-match-wins list of
dynamic redirect rules. ~20 context variables (country, device,
browser, OS, date, time, weekday, total/sub-ID clicks, referer
+ referer domain, language, logged-in, user role, random,
url_param.*, cookie.*) across 18
operators (is/is_not, contains, starts/ends_with, regex,
gt/lt/gte/lte, between, in/not_in, is_set/is_not_set,
before/after). No rule matches → fall back to the main
target URL.
A/B testing#
Engine-level split testing with weighted variants, sticky routing (a visitor keeps the same variant within a session window), bot exclusion so crawlers never skew assignment, a per-variant click breakdown in Link Analytics, an A/B badge on the Manage Links table, and inline variant configuration in the link editor. Pretty Links Pro parity, shipped.
Auto-linking engine#
- SQL-backed keyword index (two tables) with save-time conflict prevention, the index can’t hold a conflicted state, so there is nothing to scan for at runtime
- Per-keyword match mode (first / all / last / random / none); phrase matching with a
{gap:n}token; pillar boost (+15 effective priority) - Distribution caps (max per post, per link per post, per paragraph, minimum proximity, intro guard); anchor-quality validator with blocklist and link-name exceptions
- Hybrid engine, native
Dom\HTMLDocumentwalker on PHP 8.4,DOMDocumentfallback on 8.2–8.3, or a legacy regex plan/commit path (user-toggleable); astripospre-check skips posts with no candidates before the walker fires - Per-post exclusion (editor meta box, click-to-toggle column on every post-type list, and the Auto-Link page panel); broken-destination links auto-excluded at render
- Sandbox Preview with a per-candidate decision trace; bulk import; Keyword Tester; a 5-tile Link Health dashboard with a cron destination checker; 5 developer filter hooks
Content-structure visualization#
- Interactive D3.js v7 SVG graph, pan/zoom/drag/fullscreen, radial silo layout, three density presets with animated transitions, directional arrowheads, viewport-aware tooltips
- Focus mode, silo-focus mode (a cluster plus its one-hop cross-silo bridges), toggleable L1–L5 levels, shortest-path finder, Click Heatmap overlay, draft-visibility toggle, node-position persistence, dark mode
- 14 pluggable SEO-signal detectors auto-discovered from
src/Graph/Elements/*/element.php, detection always runs; the legend only toggles visualisation. Google-dependent detectors hide when GSC sync is off (no empty badges) - Link Gravity: iterative authority scoring (PageRank-class internally; the trademark term is never used in the UI)
- 5-level topical classification (Hub / Pillar / Cluster / Long Tails / Orphan, names customizable); SEO Health Score (0–100, grade A–F) with Top-3 issue chips; a separate Taxonomy Graph; PNG/SVG/CSV export
- Filter presets; a WP_DEBUG-gated stress generator producing a realistic 1-hub silo plus ~100 signal events across every detector type
Link Builder canvas#
A standalone D3 canvas (deep-linked from the Content Graph, gated on it) for sketching a new silo and committing it as real WordPress drafts. Role-aware nodes, a wizard with collision-free radial layout, snap-to-parent magnetic reparenting with symmetric auto-demote/promote, saved plans + recent batches in one list, JSON export/import for cross-site silo sharing, an opt-in per-batch managed-link wrapper, a depth advisory cap, and a preflight → lock → drafts → skeleton → cleanup lifecycle with one-click whole-batch deletion.
Google GA4 + Search Console#
- Full GA4 + GSC module, service account OR OAuth, daily incremental cron sync with live progress, a 16-month historical backfill (burst-mode cursor walk with quota-aware pause/retry) and a weekly rollup that compresses >90-day data
- Graph Enricher: writes per-node GSC/GA4 metrics that every Content Graph element and overlay reads
- Per-post Google Performance meta box (impressions, clicks, CTR, average position vs. site average)
- Search Performance admin page, the Link Analytics layout for organic search: 4 summary tiles, a dual-axis daily trend chart, Top Pages + Top Queries, 7/30/90-day ranges, all served from the cached tables (zero API calls per view)
- Full ~250-entry country filter injected into GSC fetch payloads
Rank Tracker#
A passive rank tracker built entirely on cached GSC data, no SERP API, no per-query billing, no manual keyword entry. Day-1 auto-seed from the existing GSC cache, dual-window snapshots (30-day smoothed + 3-day narrow), an optional pin system, an SE-Ranking-inspired dashboard (position-distribution donut, tabbed time-series, per-keyword drill-down with cannibalization warnings), post-editor delta sparklines, Content Graph rank-mover overlays, and four parallel alert surfaces (admin notice, email digest, webhook, dashboard widget).
Webhooks, anchor-text tags, GeoIP, shorteners, bots#
- Webhooks: outbound POST on link-health changes and rank-mover events
- Anchor-text tags: short evergreen tags (current year, copyright spans, post views, read time) expand inside managed-link anchor text, so an anchor like “Best deals [y]” still shows the year. The tag engine moved to Gillish Core in v2.91.30; Node keeps only this anchor-text expansion, active when Gillish Core is enabled
- GeoIP: 3 providers (Cloudflare header, MaxMind, IP2Location) behind the
gillish_node_resolve_country_codefilter chain, the signal Cairnstone’s Conditional Content reads when both plugins are active - Shorteners: 5 providers (Bitly, Rebrandly, Short.io, TinyURL, YOURLS)
- Bot detection: a ~1500-pattern CrawlerDetect bundle behind the
gillish_node_is_botfilter
Privacy, RBAC, REST, WP-CLI, QR#
- Privacy/GDPR: WordPress Privacy API exporter + eraser
- RBAC: a custom-capability system with 5 plugin-specific caps, installed on activation and self-healed every admin request; managed links already auth on
gillish_node_manage_linksso agency workflows work withoutmanage_options - REST: namespace
gillish/node/v1(links CRUD + resolve, public track endpoint, analytics summary/clicks/top-links/geo/referers) - WP-CLI: backfill status/start/tick/cancel commands
- QR codes for any managed link
Architecture overview#
src/Plugin.php is the single hook registry,
every add_action/add_filter lives in
register_hooks(), so it is the one file to read when
tracing how something is wired. The Content Graph is a pluggable
pipeline: each SEO signal is a self-contained class under
src/Graph/Elements/<id>/element.php,
auto-discovered via a filter, with a topological dependency sort
, adding or removing a signal never touches the base.
Settings are declarative flat-schema arrays rendered by one
renderer. The link-rendering pipeline resolves Synced markers on
the_content at priority 8; redirects run on
template_redirect at priority 1, before any
template.
Better together with Gillish Cairnstone#
Cairnstone is the sibling plugin, a Sandbox composer plus a
block library. It is fully standalone (its own GeoLite2 database,
its own crawler list). When both plugins are
active, Node becomes the intelligence hub the Cairnstone design layer
calls into, all gated behind
class_exists( '\\Gillish\\Node\\Plugin', false ) so a
Cairnstone-only install simply skips it:
- Managed Link Intelligence: a link-bearing Cairnstone block resolves a Node managed-link ID at render time and inherits the post-redirect destination, preferred anchor text, and
relattributes (one-way: Node exposes, Cairnstone reads) - Automated Affiliate Disclosure: Node detects when a post contains sponsored Cairnstone-block links and idempotently injects Cairnstone’s Affiliate Disclosure block (FTC compliance for free; per-post and global overrides)
- Live Link Health Bridge: Cairnstone surfaces Node’s per-link health as an editor badge and polls a compact, cache-bounded health endpoint from the Sandbox
The boundary is deliberate: Node owns the link layer (cloaking, rules, tracking, health, auto-link, graph, GSC/GA4); Cairnstone owns the design layer (Sandbox, blocks, conditional rendering). No service locator, no shared library, no Composer coupling, they compose cleanly when the user owns both, coherent through the shared Gillish design language and the better-together bridges above, while each keeps its own separate admin menu and neither depends on the other.
What Node deliberately does NOT do#
Explicit non-goals, documented so scope debates don’t re-open every release:
- Affiliate-program management (commission tracking/payouts), a different product category
- Block library / page design: that is Gillish Cairnstone; Node owns the link layer Cairnstone calls into
- Conditional content: shipped in Node v2.79–v2.90, extracted to Cairnstone in v2.91; Node still owns the GeoLite2 country signal
- Regex / wildcard redirect source patterns: “a managed redirect is a WordPress post” is the identity; pattern-matching is Redirection / Rank Math territory
- Amazon-only product displays: tracked as a competitive gap, depends on a future PA-API module
- AI / LLM features: deferred indefinitely per a standing decision
- Multisite / network activation: single-site install only
Competitive landscape#
Node competes in two spaces. In link management +
affiliate cloaking (Pretty Links,
ThirstyAffiliates, LinkCentral, Lasso, Easy Affiliate) it
differentiates with Synced links (no competitor ships the
DB-resolved model), the dynamic-rules engine, Content Graph
integration, built-in A/B testing, and import/export round-trip
with undo. In internal-linking analysis
(Link Whisper, Internal Link Juicer, Linkilo,
Yoast) it differentiates with the interactive D3 graph, the
pluggable 14-detector pipeline, Link Gravity scoring, the
SQL-backed auto-link index, five match modes plus {gap:n}
phrase matching, and save-time conflict prevention.
Acknowledged gaps: no Amazon PA-API / product-showcase blocks yet (Lasso / AAWP territory, tracked); regex/wildcard redirect patterns are intentionally out of scope (Redirection / Rank Math territory).
Roadmap#
Node is feature-complete and in maintenance. The phase-by-phase
build history, the active roadmap (Cairnstone Synergy hooks +
REST + Disclosure injector, the 9-competitor migration wizard,
competitive-parity closure, Lemon Squeezy licensing, the A/B
enhancement backlog), and how the in-repo plans/
corpus is organised are on the
Foundation plan page. Active plans
live in plans/; shipped work is summarised in
plans/DONE-WORK.md. This brief is the elevator-pitch
view, the code is the source of truth.