Skip to content

Releases: marutisoftwaresolutions/inkwell

v1.0.5

Choose a tag to compare

@marutisoftwaresolutions marutisoftwaresolutions released this 05 Sep 10:09

Inkwell v1.0.5

Release date: 2026-09-05
Type: Feature release (backward-compatible — existing tenants keep their layout, theme, content and settings)

v1.0.4 gave operators visibility into content that needs work. v1.0.5 turns that into
prevention: the platform now stops broken structured data from shipping, tells you what a post
is missing before an answer engine passes it over, proposes the internal links it should carry, and
shows which AI crawlers are actually reading the site. It also adds the Redirects manager that
v1.0.4's 301/302/410 support was missing, publisher identity settings for the knowledge graph,
an import dry-run, and a set of indexation fixes for category and tag archives.

Highlights

✅ Pre-publish structured-data linter

Every post is validated before it goes live. A post whose structured data would not validate is
saved as a draft instead of published, with the reason shown in the editor.

  • Catches the failure that used to be invisible: a malformed FAQ, Key Facts, How-To or roundup
    block was silently swallowed by the renderer, so a broken block looked identical to a missing one.
  • Enforces the rules that matter for search: every roundup entry must link the vendor's real site
    with an absolute URL, the internal review path stays in ctaUrl, scores sit on the 0–10 scale,
    and a first-party product may be featured but never carries a self-authored score.
  • Advisory issues (a thin FAQ answer, a duplicate label, a single-step How-To, duplicate ranks)
    surface as warnings and never block publishing.

📊 Answer-engine readiness score

Every post now carries a 0–100 score for how ready it is to be quoted by an answer engine,
shown in the editor sidebar with a per-signal breakdown and in Admin → Content Health as a
column, a tile and a filter.

  • The linter says what is broken; this says what is absent: answer capsule, Key Facts, FAQ,
    meta description, section headings, internal links, freshness dates, depth and feature image,
    each weighted by how much it contributes to being cited and each carrying concrete advice.
  • The answer capsule carries the most weight, because it is the passage an engine actually lifts.
  • Advisory only — it never blocks a publish. A short note that scores low may be exactly
    right, and forcing every post into one shape would produce filler.
  • Nothing unobservable is scored: no accuracy or authority judgement, no invented ranking prediction.

💬 Answer capsules

A post can now carry a short direct answer, written to be quoted, shown above the article body
and emitted as schema.org abstract on the BlogPosting. Answer engines and featured snippets lift
passages rather than pages; the capsule is the sentence they take. It is visible copy, never hidden
text, so the same words serve the reader. Authored in the editor sidebar with a 40–60 word target;
the linter warns when a capsule is too short to be an answer or too long to be quoted whole. Posts
without one are unaffected.

🔗 Internal-link suggester and Link Audit

  • In the editor: the sidebar proposes links in both directions while you write — published
    posts this one should link to, and posts that should link back to it — with the reason for each
    suggestion (a product this post names but has not linked, or a shared topic). It shows how the
    post stands against the two-in / two-out minimum and flags it as an orphan when nothing links to
    it. Suggestions are ranked, never applied, and loaded on demand.
  • Admin → Link Audit scans every published post and page for internal links that dead-end,
    point at a URL retired with 410, or take an avoidable redirect hop. Dead ends and retired targets
    are separated from redirect hops because they cost different things; multi-hop chains are called
    out so the final destination can be linked directly. Generated routes and external links are out
    of scope, so the report contains faults rather than false positives. Read-only; Editor and Admin.

↪️ Redirects manager with a triaged 404 worklist

v1.0.4 shipped 301/302/410 redirect rules with no way to manage them except SQL. Admin →
Redirects
now lists, creates, edits and removes rules — including the ones written automatically
by post renames and imports, which were previously invisible.

Alongside it, the 404 log becomes a worklist. Most 404s on a public site are vulnerability
scanners, so instead of listing all of them the screen asks for evidence a 404 is reader-facing:
another page links to it, or it is close enough to a real published slug to be a rename, a typo or a
stale external link. Those rank first with a one-click 301 to the suggested post or 410 to
retire it
; everything else is collapsed away, since repeated probing is the IP Firewall's business.
Rules that would loop, chain into another redirect, or target an already-retired URL are refused with
the real destination named. Admin-only, anti-forgery protected, every change audited.

🤖 AI-crawler analytics

Admin → AI Crawlers reports which AI answer engines and search crawlers actually fetch the site,
how often, and which pages they read. This traffic passed through the site every day and was
discarded, because bot hits must not pollute human analytics; it is now recorded to its own
CrawlerVisits table, so existing visitor numbers are untouched.

  • Shows a daily trend, the pages drawing the most AI attention, and GPTBot, ClaudeBot,
    PerplexityBot, Google-Extended, Applebot-Extended, CCBot, Bytespider and the rest, with each
    operator's published purpose, separating AI engines from ordinary search crawlers.
  • Every engine Inkwell can name is listed including those with zero visits — "no AI engine has
    read this site" is the most useful finding the page can report.
  • A user-agent is self-asserted; the report says it shows claimed identity and is never used to
    allow or deny a request.

🏛️ Publisher identity for the knowledge graph

Admin → Settings gains the entity fields search and answer engines use to work out who a
publication is: publisher type (Organization or Person), legal name, founder, founding date,
authority profiles (Wikipedia, Wikidata, a company register, an ORCID) and an identity statement
written for answer engines. Profiles join the existing social links in sameAs; the facts become
legalName / founder / foundingDate on the publisher node; the statement is published verbatim in
the Identity section of llms.txt. This matters most on a domain that previously published
something else. Every field is optional and nothing is inferred — an unparseable date or a
non-absolute profile URL is dropped rather than guessed at. Blogs that never open Settings are
unchanged.

🔍 Import dry-run

The import wizard now offers Preview first alongside Start import. It shows exactly what a
WordPress or Ghost file would do before anything is written: posts and pages to be created, slugs
that would be suffixed because of a clash, content that would be overwritten, items skipped and
why, images to be fetched, and internal links that would not resolve. Overwrites are called out
prominently. The preview is read-only by construction — it uses a separate analyzer with no write
path — and it predicts rather than simulates: images are counted, not downloaded.

🗂️ Topic index in llms-full.txt

The AI content index now carries a ## Topics section alongside the category grouping, listing each
tag with its archive URL, article count and the titles it covers. Only topics deep enough to have an
indexable archive are listed, so the file never points a crawler at a noindex page. Generated per
tenant; blogs with no tags are unaffected.

Changed

  • Page titles no longer overflow the search-result budget. The " | Site Name" suffix is now
    appended only when the finished title still fits inside roughly 60 characters. Previously it was
    added unconditionally, so search engines cut off the part that distinguished the page while keeping
    boilerplate they already show as the site name. Titles that already contain the site name, and
    category/tag/series archives, are unchanged.
  • Category and tag links go straight to the canonical archive. Selecting a single category or
    tag from the home-feed filters links directly to /category/{slug} or /tag/{slug} instead of a
    query-string URL that 301s there.
  • Multi-tag filter links from an archive no longer mint duplicate URLs. Selecting a second tag
    while on /tag/{slug} built a relative link the archive route ignored, silently dropping the
    second selection and generating a crawlable near-duplicate per chip. Those links are now rooted at
    the home feed, where multi-value filtering actually runs.

Fixed

  • Unknown category and tag URLs return 404. /category/{slug} and /tag/{slug} answered 200
    for any slug, rendering an empty archive titled from the raw slug — an unbounded soft-404 surface.
    Both routes now check the taxonomy exists first, as /author/{slug} already did.
  • Empty categories and tags are no longer offered as filters. The public filter chips listed
    every category and tag in the database, including those with no published posts. Reader-facing
    lists now count published posts only; admin screens still show drafts and empty taxonomies.
  • Tag archives held up only by drafts no longer reach the sitemap. The indexability test
    counted unpublished posts, so a tag with three drafts could be advertised while its archive
    answered noindex.
  • Archive pagination no longer repeats the facet in the query string. Page 2 of /tag/{slug}
    linked to ?page=2&tags={slug}; archive pagers now emit ?page=2 alone.
  • **A fresh install from DBScripts/init.sql now produces a complete...
Read more

v1.0.4

Choose a tag to compare

@marutisoftwaresolutions marutisoftwaresolutions released this 15 Aug 07:42

Inkwell v1.0.4

Release date: 2026-08-15
Type: Feature release (backward-compatible — existing tenants keep their layout, theme, content and settings)

This release makes an Inkwell blog safer to run and easier to maintain. It adds an IP Firewall
that blocks hostile traffic on its own, a Content Health dashboard that turns post maintenance
into a worklist, and a set of indexation controls — 410 retirement, filter-URL canonicalization,
and depth-gated tag archives. It also ships the WordPress & Ghost importer and Series &
collections
, plus fixes for HEAD requests and archive pages.

Highlights

🛡️ IP Firewall — automatic blocking

Public sites are probed continuously. Every failing request is now weighed by a threat scorer, and
addresses that cross the threshold block themselves.

  • Exploit probes (.php, wp-admin, .env, .git, path traversal, SQL-injection payloads,
    app-server consoles) score 5; a rejected sign-in scores 3; an ordinary 404 scores 1.
  • At 10 points within 10 minutes (both configurable) the address is blocked — 24 hours on the
    first offense, a week on the second, permanently on the third — and further requests get a bare
    403 before routing, views or logging run.
  • Admin → Security shows active blocks with reason and denied-request counts, a live watchlist of
    addresses building a score, and the thresholds, allowlist and alert settings. Addresses can be
    blocked or allowlisted by hand, and Admin → Error Monitor now records each signature's last
    client IP with a one-click Block.
  • Rules live in a new IpFirewallRules table so blocks survive a restart; enforcement reads an
    in-memory snapshot, so an ongoing attack costs no per-request database work.

Safe by default: loopback and private ranges are never blocked, signed-in staff are never scored,
search and AI crawlers (Googlebot, Bingbot, GPTBot, ClaudeBot, …) are never blocked for 404s, proxy
headers are ignored unless you declare a trusted proxy, and the firewall fails open if the
database is unreachable. Every rule change is written to the audit trail.

🩺 Content Health dashboard

Admin → Content Health lists published posts that need maintenance: reviews overdue or due within
30 days, posts never verified, posts missing a Key Facts or FAQ block, meta descriptions that are
missing or long enough to truncate in search results, and titles still promising a year that has
passed. Tiles double as filters and every row links to the editor. Read-only — it surfaces work, it
never edits. Available to Editors and Admins.

🔎 Indexation controls

  • Retire a URL with 410 Gone. Redirect rules now carry a status code — 301 (default), 302, or
    410. A 410 serves a dedicated "no longer available" page and tells search engines the removal
    was deliberate, which de-indexes far faster than a 404 that keeps getting re-crawled.
  • Filter-URL canonicalization. A single-value home-feed filter (/?tags=x, /?category=y) now
    301s to the matching /tag/{slug} or /category/{slug} archive, so filter permutations stop
    competing as separate indexed URLs. Multi-value combinations are unchanged, pagination survives the
    redirect, and only well-formed slugs redirect so a crafted query string can't become a redirect target.
  • Tag archives are indexable once they have depth. An archive with at least three posts becomes a
    real topic page; thinner ones stay noindex, and sitemap.xml now lists only archives that are
    actually indexable — it no longer advertises noindex URLs.

📥 WordPress & Ghost importer

Migrate an existing blog from Admin → Import: upload a WordPress WXR (.xml) or Ghost
JSON
(.json/.zip) export, preview it, choose exactly what to bring in (posts, pages,
categories/tags, images, comments, with a published-only filter and slug-conflict policy), then run a
batched, resumable import with live progress, a per-item exception log and a downloadable .xlsx
report. Images are downloaded (SSRF-guarded), converted to WebP and re-linked; Ghost images
bundled in a .zip are read straight from the archive; old permalinks become redirects; imported HTML
is sanitized. An import can be undone. Admin-only.

📚 Series & collections

Group posts into an ordered reading sequence. Admin → Series creates and orders them; each series
gets a public /series/{slug} page plus a /series directory, member posts show a "Part N of M ·
Previous / Next"
banner, and series emit CollectionPage + ordered ItemList + BreadcrumbList
structured data and appear in the sitemap.

🖼️ Shared image pipeline

The automatic WebP conversion introduced for uploads is now a shared service reused by the importer,
so every image entering the media library — uploaded or imported — is optimized through one path.

🐛 Fixes

  • HEAD requests no longer return 405. Routing matches methods exactly and every page route is
    GET-only, so every HEAD request was refused — including HEAD / and HEAD /robots.txt, and even
    HEAD for a URL that does not exist (405 instead of 404). Uptime monitors, link checkers and some
    crawlers rely on HEAD. It now returns exactly what GET would: same status, same headers, no body.
    HEAD requests are excluded from page-view analytics and logged as HEAD, not GET.
  • Category and tag archives render the full listing chrome again. They share the home-feed view
    but never populated its filter chips, popular-posts sidebar or the configured layout, so those pages
    silently fell back to defaults. All three listing routes now populate the same view data, and an
    archive seeds its own filter selection so adding a second filter keeps the first.
  • Post pages no longer 500 when a post has no categories or tags. The related-posts query emitted
    an invalid ORDER BY (0); it now falls back to recency ordering.

Upgrade notes

  • Schema: the new IpFirewallRules table, the Redirects.StatusCode column, ErrorLogs.LastIpAddress,
    and the Series / SeriesPosts and ImportJobs / ImportItems tables are created automatically on
    startup by MigrationService. Dated scripts in DBScripts/ mirror them for manual or production
    application — run them with sqlcmd -f 65001 (UTF-8; omitting it corrupts em dashes and curly quotes).
  • The IP Firewall is on by default with conservative settings (10 points / 10 minutes, 24-hour first
    block, crawler protection on, proxy headers not trusted). Review it in Admin → Security. If your
    site sits behind Cloudflare or another reverse proxy, enable Trust proxy headers there — otherwise
    every visitor appears as the proxy's address.
  • Backward-compatible: existing redirect rules keep serving 301, tag archives that lack depth keep
    their previous noindex behaviour, and a tenant that never opens the new screens is unaffected. No
    settings changes are required.

See CHANGELOG.md for the complete list.

v1.0.2

Choose a tag to compare

@marutisoftwaresolutions marutisoftwaresolutions released this 29 Jul 05:16

Inkwell v1.0.2

Release date: 2026-07-29
Type: Feature release (backward-compatible — existing tenants keep their layout, theme, and content)

Inkwell is a free, open-source, self-hosted, multi-tenant blogging platform built on ASP.NET Core (.NET 10) with SQL Server. v1.0.2 is a major step up in SEO / AEO (AI-search) readiness, adds a scored-roundup content system, new layouts, instant indexing, and an error-monitoring subsystem.

Highlights

🔎 SEO & AI-search overhaul

  • Rich structured data across the site: Organization, WebSite + SearchAction, CollectionPage, BlogPosting, BreadcrumbList, FAQPage, HowTo, author Person, and roundup ItemList/Review/SoftwareApplication.
  • Per-tenant llms.txt + new llms-full.txt for AI answer engines (ChatGPT, Claude, Perplexity, Gemini), a curated AI-crawler allow-list in robots.txt, and entity/disambiguation signals.
  • Author E-E-A-T pages (/author/{slug}), configurable content language (lang/hreflang/og:locale), image sitemap, sitelinks search box, and Search Console / Bing verification fields.
  • Performance: precompiled Tailwind, preloaded brand fonts, deferred non-critical scripts, LCP-prioritized images.

⚡ IndexNow instant indexing

Enable in Admin → Settings; publishing or updating a post pings Bing/Yandex/Seznam/Naver to re-crawl within minutes. Ownership key auto-generated and served at the site root. Per-tenant, off by default, best-effort.

🏆 Verdict roundup content system

A scored "Top N Best … Software" format with a dedicated layout, per-entry scores/pros/cons/CTAs, internal-cluster linking, verified outbound vendor links, first-party disclosure, and rich-result-safe ItemList/Review schema.

🎨 New layouts

The Catalog layout (single-column boxed cards, category filter bar, numbered pagination) joins the library, selectable from Admin → Theme with safe fallback for legacy values.

🚨 Error Monitor + alerts

A global exception handler and status-code logger record 4xx/5xx errors grouped by signature with occurrence counts and first/last-seen. Admin → Error Monitor lists them with filters and summary tiles; optional email alerts notify configured recipients the first time a new server error appears.

✍️ Authoring

Key Facts ("At a glance") and How-To step blocks, a live meta-description length helper, smarter related-posts/internal linking, and consistent | Brand page titles.

Upgrade notes

  • Schema: MigrationService applies all required changes on startup (new ErrorLogs table; Posts.RoundupJson / KeyFactsJson / HowToJson columns). The dated scripts in DBScripts/ mirror these for manual/prod application (sqlcmd -f 65001).
  • New settings (IndexNow, error-notification recipients, content language, verification codes) live in the settings JSON blob and default to safe/off — no action required.
  • Fully backward-compatible: no breaking changes; a tenant that upgrades without touching settings keeps its current appearance and behavior.

See CHANGELOG.md for the complete list.

v1.0.1

Choose a tag to compare

@marutisoftwaresolutions marutisoftwaresolutions released this 27 Jun 15:05
2f509b3

What's new in v1.0.1

This release is a major feature expansion over the initial v1.0.0 launch,
adding seven new subsystems, a complete design system, and full audit coverage
across every admin action.


Analytics Dashboard

Built-in page view tracking — no Google Analytics needed.

  • Captures every public page hit: path, referrer, UTM parameters (source,
    medium, campaign, term, content), IP hash, user agent, country,
    and region.
  • Traffic source classifier: Organic, Direct, Social, Paid, Email, Referral.
  • Admin-only dashboard at /admin/analytics — daily/weekly trends, top pages,
    top referrers, source breakdown.
  • Authenticated admin visits are never counted.

Audit Trail

Immutable record of every admin write action — accessible only at /admin/audit.

  • Covers all 12 admin controllers: Posts, Pages, Comments, Media, Users,
    Settings, Theme, Categories, Tags, Newsletter, Subscribers, Redirects.
  • Before/after JSON snapshots for settings and role changes.
  • Filter by entity type, action, user, and date range. CSV export up to 10,000 rows.
  • Read-only from the UI — no delete or truncate endpoint is exposed.

Inkwell Theme System

  • 10 curated color presets: Warm Cream, Ocean Breeze, Midnight Blue,
    Forest Canopy, Obsidian Rose, Terracotta Sun, Slate Professional, Ivory Gold,
    Lavender Mist, and Charcoal Modern.
  • 6+ layout variants: Magazine, Grid, Minimal, Neutral, Classic, Modern —
    independently configurable for index, navbar, and footer.
  • Live CSS variable overrides stored per-owner in CustomThemeSettings.
  • All ViewComponents have explicit supported-values guards — unknown DB values
    fall back safely, no crashes on upgrade.

Newsletter & Subscribers

  • Compose and send newsletters from /admin/newsletter.
  • Double opt-in subscriber management with confirm/unsubscribe tokens.
  • Label-based member segmentation.
  • CSV import/export at /admin/subscribers.

Redirects Manager

  • Rule-based HTTP redirects at /admin/redirects.
  • Hit count tracking per rule.

SEO Enhancements

  • OG image generation at /og-image/{slug} for every post.
  • FAQ schema (JSON-LD) stored per post via FaqJson column.
  • LastVerifiedAt / NextReviewAt fields for clinical/medical content review
    workflows.
  • _SchemaJsonLd.cshtml structured data partial on all post pages.

User Profile Enhancements

  • Credentials, Specialty, and LicenseNumber fields on author profiles —
    designed for medical and professional blogging contexts.

Bug Fixes

  • PageView tracking was silently failing — fire-and-forget middleware was
    creating scopes from the request-scoped DI container (disposed before the async
    task ran). Fixed by capturing all values synchronously and creating scopes from
    the root IServiceProvider.
  • Admin visits were being counted — middleware now checks
    context.User.Identity.IsAuthenticated and skips all authenticated requests.

Database Changes

All changes are applied automatically on startup via MigrationService.
Manual scripts are in DBScripts/ if needed.

New tables: PageViews, Members, Labels, MemberLabels, Newsletters,
Emails, MemberNewsletters, Redirects, Snippets, AuditLogs

New columns: Posts.FaqJson, Posts.LastVerifiedAt, Posts.NextReviewAt,
Users.Credentials, Users.Specialty, Users.LicenseNumber,
Categories.Color, Tags.Color, Media.OriginalFileName,
PageViews.Country, PageViews.Region


Upgrading from v1.0.0

git pull origin main
dotnet run --project Blog.Web