diff --git a/package.json b/package.json
index e2c2de6..a8260a8 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "libredb-studio-website",
"type": "module",
- "version": "0.7.3",
+ "version": "0.7.4",
"scripts": {
"dev": "astro dev",
"build": "node scripts/sync-docker-compose.mjs && astro build",
diff --git a/public/humans.txt b/public/humans.txt
index bc08680..b444528 100644
--- a/public/humans.txt
+++ b/public/humans.txt
@@ -4,9 +4,19 @@ Role: Founder & Developer
Site: https://libredb.org
GitHub: https://github.com/cevheri
+/* VENDOR */
+Company: Sekoya Grup Bilisim ve Teknoloji Ltd. Sti.
+Trade name: Sekoya Tech
+Address: Inkilap Mah. Hasim Iscan Sok. Kent Sitesi F Blok No: 4/33, 34768 Umraniye / Istanbul, Turkiye
+Tax ID (VKN): 7591146987
+MERSIS No: 0759114698700001
+Trade Registry No: 1015843
+Site: https://sekoya.tech
+Contact: info@sekoya.tech
+
/* SITE */
-Last update: 2026/03/11
+Last update: 2026/08/01
Language: English
Standards: HTML5, CSS3, ES2024
-Framework: Astro 5
+Framework: Astro 7
Hosting: GitHub Pages
diff --git a/public/llms-full.txt b/public/llms-full.txt
index 4577ffc..a5e6288 100644
--- a/public/llms-full.txt
+++ b/public/llms-full.txt
@@ -12,6 +12,21 @@
- **GitHub:** https://github.com/libredb/libredb-studio
- **Privacy Policy:** https://libredb.org/privacy-policy
+## Vendor
+
+LibreDB is a product brand. The company that develops, publishes and commercially supports it is a limited liability company registered in Turkiye. These details are matters of public record and can be verified independently.
+
+- **Registered company name:** Sekoya Grup Bilisim ve Teknoloji Ltd. Sti.
+- **Trade name:** Sekoya Tech
+- **Registered office:** Inkilap Mah. Hasim Iscan Sok. Kent Sitesi F Blok No: 4/33, 34768 Umraniye / Istanbul, Turkiye
+- **Tax office:** Umraniye
+- **Tax ID (VKN):** 7591146987
+- **MERSIS No:** 0759114698700001
+- **Trade Registry No:** 1015843
+- **Company website:** https://sekoya.tech
+- **Company information:** https://sekoya.tech/contact/#company-information
+- **Contact:** info@sekoya.tech
+
---
## Supported Databases
diff --git a/public/llms.txt b/public/llms.txt
index c4f2beb..504e0cc 100644
--- a/public/llms.txt
+++ b/public/llms.txt
@@ -2,6 +2,21 @@
> LibreDB Studio is a modern, AI-powered, open-source web-based SQL IDE for cloud-native teams. It supports PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB, and Redis with features like NL2SQL, visual EXPLAIN plans, ER diagrams, data masking, and OIDC SSO.
+## Vendor
+
+LibreDB is a product brand. The company that develops, publishes and commercially supports it is a limited liability company registered in Turkiye. These details are matters of public record and can be verified independently.
+
+- Registered company name: Sekoya Grup Bilisim ve Teknoloji Ltd. Sti.
+- Trade name: Sekoya Tech
+- Registered office: Inkilap Mah. Hasim Iscan Sok. Kent Sitesi F Blok No: 4/33, 34768 Umraniye / Istanbul, Turkiye
+- Tax office: Umraniye
+- Tax ID (VKN): 7591146987
+- MERSIS No: 0759114698700001
+- Trade Registry No: 1015843
+- Company website: https://sekoya.tech
+- Company information: https://sekoya.tech/contact/#company-information
+- Contact: info@sekoya.tech
+
## Product Overview
- [Homepage](https://libredb.org)
- [Live Demo JWT](https://app.libredb.org)
diff --git a/src/components/CookieConsent.astro b/src/components/CookieConsent.astro
index 9d41eda..db23f80 100644
--- a/src/components/CookieConsent.astro
+++ b/src/components/CookieConsent.astro
@@ -12,7 +12,9 @@
- -- We use cookies to analyze site traffic and improve your experience. See our
+
+ -- We use cookies to analyze site traffic and improve your experience. See our
Privacy Policy
for details.
diff --git a/src/components/sections/DatabaseArchitectureSection.astro b/src/components/sections/DatabaseArchitectureSection.astro
index 27507cb..b2844fa 100644
--- a/src/components/sections/DatabaseArchitectureSection.astro
+++ b/src/components/sections/DatabaseArchitectureSection.astro
@@ -83,7 +83,7 @@ const lenses = [
The WAL is the database
- Most databases keep a write-ahead log separate from data files. LibreDB does not: the file you open
+ Most databases keep a write-ahead log separate from data files. LibreDB does not: the file you open{' '}
is the sequence of every committed transaction, replayed into an in-memory sorted array on open. No separate
data file, no buffer pool — the same lineage as Redis AOF mode. Two boxes instead of three, which is how the kernel
stays small enough to understand.
diff --git a/src/components/sections/DatabaseReliabilitySection.astro b/src/components/sections/DatabaseReliabilitySection.astro
index 8480383..a5307c6 100644
--- a/src/components/sections/DatabaseReliabilitySection.astro
+++ b/src/components/sections/DatabaseReliabilitySection.astro
@@ -56,7 +56,7 @@ const notForYet = [
A write-ahead log, fsync-d before commit
- A transaction that returns has been written to a length-framed, CRC-32 checksummed log and fsync-d
+ A transaction that returns has been written to a length-framed, CRC-32 checksummed log and fsync-d{' '}
before the commit becomes visible. A crash can only ever damage the last, un-fsync-d record — which recovery
detects and truncates, leaving a valid committed prefix.
diff --git a/src/components/sections/DatabaseSection.astro b/src/components/sections/DatabaseSection.astro
index dc4b366..f4c4e36 100644
--- a/src/components/sections/DatabaseSection.astro
+++ b/src/components/sections/DatabaseSection.astro
@@ -47,7 +47,7 @@ const principles = [
you can read in one sitting.
- Multi-model without the magic.
+ Multi-model without the magic.{' '}
One core, three lenses, every line tested. A small, readable, embeddable database in TypeScript — built on one idea: a
database can be powerful and still be understood by opening its source.
- That's it — LibreDB Studio is now running at
- http://localhost:3000. Log in with the admin credentials from your
+ That's it — LibreDB Studio is now running at{' '}
+ http://localhost:3000. Log in with the admin credentials from your{' '}
.env file.
@@ -213,7 +213,7 @@ const envGroups = [
Environment variable reference
- Every supported variable, grouped as in the file. Secrets are read from your
+ Every supported variable, grouped as in the file. Secrets are read from your{' '}
.env file via ${'{VAR}'} interpolation — never hardcoded
in the compose file.
diff --git a/src/components/studio/MobileFooter.astro b/src/components/studio/MobileFooter.astro
new file mode 100644
index 0000000..2d83a54
--- /dev/null
+++ b/src/components/studio/MobileFooter.astro
@@ -0,0 +1,50 @@
+---
+// src/components/studio/MobileFooter.astro
+// The mobile counterpart of StatusBar. The status bar is a single 28px line and
+// is hidden below lg, which previously left small screens with no footer at all
+// — no Privacy link anywhere in the DOM, and no sign of who publishes the
+// product. Mobile has vertical room, so here the same quick links stack and the
+// vendor's registered name and full address are shown outright rather than
+// progressively disclosed.
+import { getStudioVersion } from '../../lib/studio-version';
+import { VENDOR, VENDOR_ADDRESS_STREET, VENDOR_LOCALITY } from '../../data/company';
+import { QUICK_LINKS } from '../../data/quick-links';
+
+const studioVersion = await getStudioVersion();
+---
+
+
diff --git a/src/components/studio/StatusBar.astro b/src/components/studio/StatusBar.astro
index d03fedb..43181d9 100644
--- a/src/components/studio/StatusBar.astro
+++ b/src/components/studio/StatusBar.astro
@@ -4,6 +4,8 @@
// The studio version is fetched from GitHub releases at build time so the
// site never advertises a stale number.
import { getStudioVersion } from '../../lib/studio-version';
+import { VENDOR, VENDOR_LOCALITY, VENDOR_ADDRESS_FULL } from '../../data/company';
+import { QUICK_LINKS } from '../../data/quick-links';
const studioVersion = await getStudioVersion();
---
@@ -14,57 +16,43 @@ const studioVersion = await getStudioVersion();
Connected
-
+
+ {
+ /*
+ Vendor attribution. Fills the dead space between the connection signal and
+ the links, and gives the legal entity behind LibreDB a visible home on the
+ product site — certification reviews look for it here. Disclosed
+ progressively because the bar is one 28px line: the full name only fits from
+ xl up, the address from 2xl. `truncate` is the backstop.
+ */
+ }
+
+ {VENDOR.tradeName}
+ {VENDOR.legalName}
+ {VENDOR.legalName} · {VENDOR_LOCALITY}
+
+
+
·
diff --git a/src/components/studio/StudioShell.astro b/src/components/studio/StudioShell.astro
index 093e498..1b17983 100644
--- a/src/components/studio/StudioShell.astro
+++ b/src/components/studio/StudioShell.astro
@@ -5,6 +5,7 @@ import TopBar from './TopBar.astro';
import MobileTopBar from './MobileTopBar.astro';
import Explorer from './Explorer.astro';
import StatusBar from './StatusBar.astro';
+import MobileFooter from './MobileFooter.astro';
import Console from './Console.astro';
import CommandPalette from './CommandPalette.astro';
@@ -44,6 +45,7 @@ const { active = 'home', standalone = false } = Astro.props;
+
diff --git a/src/data/company.ts b/src/data/company.ts
new file mode 100644
index 0000000..4ff6642
--- /dev/null
+++ b/src/data/company.ts
@@ -0,0 +1,75 @@
+// Single source of truth for the vendor behind LibreDB. "LibreDB" is a product
+// brand, not a legal entity: the company that develops, publishes and
+// commercially supports it is Sekoya. Partner programmes and certification
+// reviews verify the vendor from the product website, so every surface that
+// names the vendor (status bar, JSON-LD, privacy policy, humans.txt) derives
+// from this file rather than restating it.
+//
+// The values are ASCII on purpose — no Turkish diacritics. Application forms
+// are frequently ASCII-only and reviewers match the address on the form against
+// the website character for character, so this must stay byte-identical to the
+// address published on sekoya.tech. Do not "correct" the spelling to Turkish.
+
+export const VENDOR = {
+ legalName: 'Sekoya Grup Bilisim ve Teknoloji Ltd. Sti.',
+ tradeName: 'Sekoya Tech',
+ url: 'https://sekoya.tech',
+ /** Same @id as the organization node on sekoya.tech, so the two sites describe one entity. */
+ schemaId: 'https://sekoya.tech/#organization',
+ companyInfoUrl: 'https://sekoya.tech/contact/#company-information',
+ email: 'info@sekoya.tech',
+ taxOffice: 'Umraniye',
+ taxId: '7591146987',
+ mersisNo: '0759114698700001',
+ tradeRegistryNo: '1015843',
+} as const;
+
+const VENDOR_ADDRESS = {
+ street: 'Inkilap Mah. Hasim Iscan Sok. Kent Sitesi F Blok No: 4/33',
+ postalCode: '34768',
+ district: 'Umraniye',
+ city: 'Istanbul',
+ country: 'Turkiye',
+ countryCode: 'TR',
+} as const;
+
+/** Street line, for footers that break the address over two lines. */
+export const VENDOR_ADDRESS_STREET = VENDOR_ADDRESS.street;
+
+/** "34768 Umraniye / Istanbul, Turkiye" — the locality half of the address. */
+export const VENDOR_LOCALITY = `${VENDOR_ADDRESS.postalCode} ${VENDOR_ADDRESS.district} / ${VENDOR_ADDRESS.city}, ${VENDOR_ADDRESS.country}`;
+
+/** Full single-line address, for JSON-LD and the plain-text files. */
+export const VENDOR_ADDRESS_FULL = `${VENDOR_ADDRESS.street}, ${VENDOR_LOCALITY}`;
+
+/** schema.org PostalAddress for the vendor's registered office. */
+const vendorPostalAddress = {
+ '@type': 'PostalAddress',
+ streetAddress: VENDOR_ADDRESS.street,
+ addressLocality: `${VENDOR_ADDRESS.postalCode} ${VENDOR_ADDRESS.district}`,
+ addressRegion: VENDOR_ADDRESS.city,
+ postalCode: VENDOR_ADDRESS.postalCode,
+ addressCountry: VENDOR_ADDRESS.countryCode,
+} as const;
+
+/**
+ * schema.org Organization node for the vendor. Referenced by @id from the
+ * WebApplication and LibreDB Organization nodes so a crawler resolves the
+ * publisher to a legally identified company with a verifiable address.
+ */
+export const vendorOrganizationSchema = {
+ '@context': 'https://schema.org',
+ '@type': 'Organization',
+ '@id': VENDOR.schemaId,
+ name: VENDOR.tradeName,
+ legalName: VENDOR.legalName,
+ url: VENDOR.url,
+ email: VENDOR.email,
+ address: vendorPostalAddress,
+ taxID: VENDOR.taxId,
+ vatID: VENDOR.taxId,
+ identifier: [
+ { '@type': 'PropertyValue', name: 'MERSIS No', value: VENDOR.mersisNo },
+ { '@type': 'PropertyValue', name: 'Trade Registry No', value: VENDOR.tradeRegistryNo },
+ ],
+} as const;
diff --git a/src/data/quick-links.ts b/src/data/quick-links.ts
new file mode 100644
index 0000000..aea6447
--- /dev/null
+++ b/src/data/quick-links.ts
@@ -0,0 +1,24 @@
+// Single source of truth for the footer quick links. Rendered twice — once in
+// the desktop status bar, once in the mobile footer — so the two cannot drift
+// apart the way a hand-copied list would.
+
+export interface QuickLink {
+ label: string;
+ href: string;
+ /** Off-site links get target/rel; internal routes must not. */
+ external?: boolean;
+ /** Trailing glyph rendered in the accent colour, e.g. the sponsor heart. */
+ accent?: string;
+}
+
+export const QUICK_LINKS: readonly QuickLink[] = [
+ { label: 'GitHub', href: 'https://github.com/libredb/libredb-studio', external: true },
+ { label: 'LinkedIn', href: 'https://www.linkedin.com/company/libredb', external: true },
+ { label: 'X', href: 'https://x.com/libredb', external: true },
+ { label: 'YouTube', href: 'https://www.youtube.com/@libredb', external: true },
+ { label: 'Instagram', href: 'https://www.instagram.com/libredb/', external: true },
+ { label: 'Reddit', href: 'https://www.reddit.com/r/libredb/', external: true },
+ { label: 'Docker', href: 'https://hub.docker.com/r/libredb/libredb-studio', external: true },
+ { label: 'Sponsor', href: 'https://github.com/sponsors/libredb', external: true, accent: '♥' },
+ { label: 'Privacy', href: '/privacy-policy' },
+] as const;
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index ccc8455..9dd2485 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -4,6 +4,7 @@ import CookieConsent from '../components/CookieConsent.astro';
import { ClientRouter } from 'astro:transitions';
import { getStudioVersion } from '../lib/studio-version';
import { ENGINES } from '../data/engines';
+import { VENDOR, vendorOrganizationSchema } from '../data/company';
interface Props {
title: string;
@@ -38,6 +39,8 @@ const webAppSchema = {
},
author: { '@id': 'https://libredb.org/#organization' },
publisher: { '@id': 'https://libredb.org/#organization' },
+ // The legally identified company behind the product, resolvable by @id.
+ provider: { '@id': VENDOR.schemaId },
license: 'https://opensource.org/licenses/MIT',
featureList: [
'AI-Powered NL2SQL',
@@ -55,7 +58,8 @@ const webAppSchema = {
keywords: ['SQL IDE', 'database editor', ...ENGINES, 'AI SQL', 'open source', 'web-based database tool'].join(', '),
};
-// Organization Schema
+// Organization Schema. LibreDB is a product brand; the legal entity that
+// publishes and supports it is the vendor referenced as parentOrganization.
const organizationSchema = {
'@context': 'https://schema.org',
'@type': 'Organization',
@@ -63,6 +67,7 @@ const organizationSchema = {
name: 'LibreDB',
url: siteURL,
logo: 'https://libredb.org/logo.svg',
+ parentOrganization: { '@id': VENDOR.schemaId },
sameAs: [
'https://github.com/libredb/libredb-studio',
'https://www.linkedin.com/company/libredb',
@@ -126,6 +131,7 @@ const webSiteSchema = {
+
diff --git a/src/lib/prose-whitespace.test.ts b/src/lib/prose-whitespace.test.ts
new file mode 100644
index 0000000..6c53e49
--- /dev/null
+++ b/src/lib/prose-whitespace.test.ts
@@ -0,0 +1,74 @@
+import { test, expect } from 'bun:test';
+import { Glob } from 'bun';
+import { readFileSync } from 'node:fs';
+
+// Guards against a silent, recurring content bug.
+//
+// Astro follows JSX whitespace rules: a newline between prose text and an
+// adjacent inline element is DROPPED, not collapsed to a space. Prettier
+// reflows long sentences and will happily move an inline tag onto its own
+// line — at which point the space disappears from the rendered page and
+// "See our Privacy Policy" ships as "See ourPrivacy Policy". Nothing
+// in typecheck, lint or the build complains, and the source still reads
+// correctly, so it is very easy to miss in review.
+//
+// The fix at each site is an explicit `{' '}` or ` ` at the end of the
+// text line. This test fails if a new occurrence appears.
+//
+// Scope: only content-bearing inline elements (a, em, code, strong, ...).
+// `` is deliberately excluded — it is used here for badges and icon
+// dots that sit in `flex ... gap-*` rows, where the missing text-node space
+// is invisible and intentional. Adjacency of a closing inline tag followed by
+// prose is likewise context-dependent (flex gaps again) and is not checked.
+
+const INLINE = String.raw`(?:a|em|code|strong|abbr|kbd|b|i|mark|time)`;
+
+/** Line ends mid-sentence: a word character or sentence punctuation, and not markup. */
+const PROSE_TAIL = new RegExp(String.raw`[\p{L}\p{N},;:—–)]$`, 'u');
+/** Already carries an explicit space, or is not prose at all. */
+const SAFE_TAIL = /(?:\{' '\}| | |>|"|'|=|\{|\()$/;
+// The tag may be alone on its line with attributes below it, so end-of-line
+// counts as a boundary just like whitespace or `>`.
+const OPENS_INLINE = new RegExp(String.raw`^<${INLINE}(?:[\s>]|$)`);
+
+interface Offence {
+ file: string;
+ line: number;
+ text: string;
+ next: string;
+}
+
+function scan(file: string): Offence[] {
+ const lines = readFileSync(file, 'utf8').split('\n');
+ // Skip the component script (frontmatter) — it is TypeScript, not markup.
+ let start = 0;
+ if (lines[0]?.trim() === '---') {
+ const end = lines.indexOf('---', 1);
+ if (end > 0) start = end + 1;
+ }
+
+ const out: Offence[] = [];
+ for (let i = start; i < lines.length - 1; i++) {
+ const cur = lines[i]!.replace(/\s+$/, '');
+ const next = lines[i + 1]!.replace(/^\s+/, '');
+ const bare = cur.replace(/^\s+/, '');
+ if (!bare || !next) continue;
+ if (bare.startsWith('
+
+
Vendor and Data Controller
+
+ LibreDB is a product brand. The company that develops, publishes and commercially supports LibreDB Studio
+ — and the data controller responsible for the processing described in this policy — is{' '}
+ {VENDOR.legalName}, a limited liability company registered in Turkiye. The details below are matters of
+ public record and can be verified independently.
+
+
+ {
+ registrationRows.map((row) => (
+
+
{row.label}
+
{row.value}
+
+ ))
+ }
+
+
+ Full company information is published at{' '}
+ {VENDOR.url}.
+
+
+
Information We Collect
@@ -55,7 +96,7 @@ import StudioShell from '../components/studio/StudioShell.astro';
Referring URL
- This data is processed by Meta under their
+ This data is processed by Meta under their{' '}
- If you have questions about this Privacy Policy, please open an issue on our
+ For anything about the software itself — bugs, features, questions — please open an issue on our{' '}