ElfGloss is being migrated from the Parf Edhellen Laravel fork into a source-driven Go dictionary service.
The target product has:
- no public user accounts
- no OAuth, passkeys, or sessions
- no discussions, feeds, comments, likes, follows, or profiles
- no web admin area
- host-only import/admin commands
- read-only public dictionary/search APIs
- dictionary content owned by upstream sources and import runs
The legacy Laravel/PHP runtime is inactive. The old src/ tree is retained as
source-of-truth material for UI and import behavior while runtime serving moves
to Go. Source mappings and import paths now live in the Go backend.
The repo includes a Docker/Compose setup with two paths:
elfgloss: starts from an app image with no bundled database and builds/data/elfgloss.dbin a named volume on first run.elfgloss-seeded: builds a direct-source database into the image itself.
docker compose up --build elfgloss
docker compose --profile seeded build elfgloss-seeded
docker compose --profile seeded up elfgloss-seededThe containers install the compiled Go service, Ruby, and the official
Glaemscribe CLI gem used by transcription. See
docs/docker-quickstart.md for legacy SQL,
rebuild, seeded-image, and production-gate options.
Retained import groups currently encoded:
- Eldamo
- Eldamo - neologism/adaptations
- Eldamo - neologism/reconstructions
- Mellonath Daeron
- Neologism
- Parma Eldalamberon 17 Sindarin Corpus
- Parviphith
- Quettaparma Quenyallo
- SINDICT
- Tolkiendil Compound Sindarin Names
Excluded old UI/community/status groups currently encoded pending catalog review:
- Any group
- Subject of debate
- Verified and confirmed
The active backend is in backend/.
cd backend
go test ./...
go run ./cmd/elfgloss inspect-legacy-sql -file old-elfdict.sql -source elfdict-legacy-sources
go run ./cmd/elfgloss source-manifest-template -out imports/sources.json -legacy-sql imports/old-elfdict.sql
go run ./cmd/elfgloss import legacy-sources -db data/elfgloss.db -file old-elfdict.sql
go run ./cmd/elfgloss build-data -db data/elfgloss.db -legacy-sql old-elfdict.sql
go run ./cmd/elfgloss build-data -db data/elfgloss.db -legacy-sources -legacy-sql old-elfdict.sql
go run ./cmd/elfgloss import all -db data/elfgloss.db -legacy-sql old-elfdict.sql
go run ./cmd/elfgloss import source -db data/elfgloss.db -source eldamo
go run ./cmd/elfgloss import source -db data/elfgloss.db -source quettaparma-quenyallo
go run ./cmd/elfgloss import source -db data/elfgloss.db -source parviphith
go run ./cmd/elfgloss import source -db data/elfgloss.db -source sindict
go run ./cmd/elfgloss import source -db data/elfgloss.db -source pe17-sindarin-corpus
go run ./cmd/elfgloss import source -db data/elfgloss.db -source neologism -file old-elfdict.sql
go run ./cmd/elfgloss import source -db data/elfgloss.db -source mellonath-daeron -file old-elfdict.sql
go run ./cmd/elfgloss audit -db data/elfgloss.db
go run ./cmd/elfgloss audit -db data/elfgloss.db -require-all-sources
go run ./cmd/elfgloss audit-code -root ..
go run ./cmd/elfgloss release-report -db data/elfgloss.db -root .. -source-url https://example.com/your-fork-source -source-review-file imports/source-review.json -legacy-sql old-elfdict.sql -require-legacy-sql-comparison
go run ./cmd/elfgloss serve -db data/elfgloss.db -addr :8080 -source-url https://example.com/your-fork-source -source-review-file imports/source-review.json -require-runtime-integrationsThe Go service serves the replacement dictionary browser at:
http://localhost:8080/
The browser uses the same read-only APIs as external clients. It exposes source,
group, language, search, and entry detail views, and intentionally has no
account, discussion, profile, or admin workflow. The serving command opens the
bbolt database read-only; imports and rebuilds must run through host CLI
commands before serving. By default, serve also runs the strict retained-source
database audit and validates the fork source URL at startup. It refuses to serve
partial, invalid, or unattributed data by default. Use -allow-partial and
-allow-missing-source-url only for local development databases.
The Eldamo importer reads the upstream XML data model directly from:
https://eldamo.org/content/data-model/eldamo-data.xml
The current Eldamo XML imports 35,900 source word entries. Its reference and inflection forms are indexed as searchable aliases for search and autocomplete without inflating the source-owned entry count.
Quettaparma Quenyallo and Parviphith import directly from the upstream Ardalambion Word documents:
go run ./cmd/elfgloss import source -db data/elfgloss.db -source quettaparma-quenyallo
go run ./cmd/elfgloss import source -db data/elfgloss.db -source parviphithPE17 imports directly from David Giraudeau's upstream PDF:
go run ./cmd/elfgloss import source -db data/elfgloss.db -source pe17-sindarin-corpusSINDICT imports directly from the upstream TEI XML maintained by The Sindarin Dictionary Project:
go run ./cmd/elfgloss import source -db data/elfgloss.db -source sindictMellonath Daeron and the retained Neologism group import from a full legacy
ElfDict SQL export because no direct public source dump is present in this repo.
The importers keep only known source/import account rows by default, use account
data only as an exclusion filter, and do not store account ownership. Mellonath
can also be imported from a curated source-owned JSONL file with -format jsonl.
For old JSONL generated for ed-import:dictionary --external-id-prefix, pass the
same prefix to -external-id-prefix or set legacyExternalIdPrefix in a
manifest so source entry IDs remain stable.
Tolkiendil Compound Sindarin Names can be imported directly from the upstream HTML page:
go run ./cmd/elfgloss import source `
-db data/elfgloss.db `
-source tolkiendil-compound-sindarin-namesLegacy ElfDict/SinDict-style MySQL dumps can be imported without storing
accounts or community rows. Both old glosses/translations dumps and renamed
lexical_entries/glosses dumps are supported. Cataloged source imports pass
source-specific group and account allowlists into the SQL parser, so a broad old
dump cannot be accidentally imported into the wrong retained source. To replay a
complete old dump into the real retained source keys, use:
go run ./cmd/elfgloss import legacy-sources `
-db data/elfgloss.db `
-file .\imports\old-elfdict.sqlThis command is strict by default: it fails if any retained source key has no
matching retained rows in the dump. Use -allow-skipped only for development
inspection of partial or placeholder SQL files. Strict runs import into a
temporary bbolt file and replace the target only after the full retained-source
split succeeds, so an incomplete dump does not leave partial data behind. The
deleted model/2. data.sql file in this fork is bootstrap auth/role seed data,
not the full old dictionary export needed for this command.
Archived SINDICT SQL can still be recovered explicitly with -format legacy-sql:
go run ./cmd/elfgloss import source `
-db data/elfgloss.db `
-source sindict `
-format legacy-sql `
-file .\imports\old-elfdict.sqlBefore importing a full old dump, use the host-only inspection command to see what will be retained and what will be excluded by group/account filters:
go run ./cmd/elfgloss inspect-legacy-sql `
-file .\imports\old-elfdict.sql `
-source elfdict-legacy-sourcesThe report counts retained rows, deleted rows, rejected moderation rows,
excluded community/status group rows, excluded group-filter rows, excluded
account-owned rows, invalid rows, and the same counts per legacy group. After
importing, pass -db data/elfgloss.db
with a catalog source key such as neologism or mellonath-daeron to compare
that source's retained legacy rows against the stored bbolt source entry count.
The legacy SQL parser supports ordinary MySQL dump inserts, INSERT IGNORE,
database-qualified table names, and column-listed
INSERT INTO table (columns...) VALUES ... statements for the dictionary tables
it imports.
For ad hoc single-source SQL imports, import legacy-sql requires -source
or -source-key with a retained catalog source key and applies that source's
cataloged group/account allowlists. Repeatable -include-group "Group name"
filters can narrow the source's own group allowlist, but cannot widen an import
to another source's group. By default it reads the old accounts table only to
discover known source/import account IDs such as Imported and Eldamo Import.
The known source/import account names are catalog metadata on the retained
source paths. If no usable account table/source-account metadata is present,
legacy SQL rows are excluded by default because source ownership cannot be
proven. Use -allow-account-id-1-fallback only for curated account-less SQL
known to contain source/import rows under account_id = 1. Arbitrary legacy
account IDs are not importable; old account columns are used only as a
source-row exclusion filter. Legacy dictionary row comments values are
discarded during SQL import; source citations and etymology continue to import
as source references.
To import all available sources in one run, use normal build-data for a fresh
rebuild from current upstreams plus legacy-only groups. For a pinned or
reviewable source bundle, generate a catalog-derived manifest with
source-manifest-template; it lists every retained dictionary source, direct
upstream URL, parser format, and the legacy SQL file for Neologism and Mellonath
Daeron. The checked-in
sources.manifest.example.json
shows the same shape. Use build-data -legacy-sources only when you
intentionally want to replay a complete old ElfDict SQL dump into retained
source keys:
go run ./cmd/elfgloss source-manifest-template `
-out .\imports\sources.json `
-legacy-sql .\imports\old-elfdict.sql
go run ./cmd/elfgloss build-data `
-db data/elfgloss.db `
-legacy-sql .\imports\old-elfdict.sql
go run ./cmd/elfgloss build-data `
-db data/elfgloss.db `
-legacy-sources `
-legacy-sql .\imports\old-elfdict.sql
go run ./cmd/elfgloss build-data `
-db data/elfgloss.db `
-manifest .\imports\sources.json
go run ./cmd/elfgloss import all `
-db data/elfgloss.db `
-legacy-sql .\imports\old-elfdict.sql
go run ./cmd/elfgloss import legacy-sources `
-db data/elfgloss.db `
-file .\imports\old-elfdict.sql
go run ./cmd/elfgloss import manifest `
-db data/elfgloss.db `
-file .\imports\sources.jsonimport manifest applies the listed sources against a temporary copy of the
target database and replaces the target only after every manifest source imports
successfully. A failed manifest leaves the existing database unchanged while a
successful manifest preserves existing sources not listed in the manifest.
Manifest entries support legacyExternalIdPrefix for legacy ElfDict JSONL
source bundles. Legacy JSONL account fields are accepted only when they include
a known source/import account name for the catalog source; bare
account_id = 1 rows are excluded by default. Legacy JSONL rows marked "-"
are treated as old moderation rejections and skipped; modern source-owned JSONL
should use sourceEntryId and omit old account fields when a source needs to
preserve its own markings:
{"key":"mellonath-daeron","file":"mellonath.jsonl","format":"jsonl","legacyExternalIdPrefix":"md"}Standalone JSONL imports are also catalog-bound; pass a retained source key and keep JSONL group names within that source's catalog allowlist:
go run ./cmd/elfgloss import jsonl `
-db data/elfgloss.db `
-file .\imports\sindict.jsonl `
-source sindictOnly jsonl and legacy-sql are generic format overrides. Direct upstream
formats such as eldamo, sindict-tei, pe17-pdf, and tolkiendil-html are
source-specific and must match the target source's catalog default.
Without -legacy-sql, import all fails by default after reporting Neologism
and Mellonath Daeron as skipped because those retained groups require a full old
ElfDict SQL export or curated JSONL source. Use -allow-skipped only for a
development direct-source smoke import. That strict missing-dump failure happens
before any direct importer runs, so it does not leave a partial database behind.
When it does run, import all imports into a temporary bbolt file and replaces
the target only after the full command succeeds; a failed source import leaves
the previous target database in place.
The catalog marks each retained dictionary source as either direct-upstream or
legacy-sql-required; import all is derived from that catalog metadata.
For production builds, prefer build-data: it imports from import all or an
exact manifest into a temporary bbolt file, fails if retained sources are
skipped, runs audit -require-all-sources, and replaces the target database only
after the build has passed. A failed build leaves the previous target database in
place.
Preserved source paths and groups are inspectable from the host:
go run ./cmd/elfgloss catalog
go run ./cmd/elfgloss catalog -coverage
go run ./cmd/elfgloss integrations
go run ./cmd/elfgloss integrations -check
go run ./cmd/elfgloss inflect -language quenya -word auta
go run ./cmd/elfgloss inflect -language sindarin -word car
go run ./cmd/elfgloss transcribe -mode quenya -text "Elen sila"
go run ./cmd/elfgloss catalog -groups
go run ./cmd/elfgloss catalog -groups -include-excluded
go run ./cmd/elfgloss inspect-legacy-sql -file .\imports\old-elfdict.sql -source elfdict-legacy-sources
go run ./cmd/elfgloss inspect-legacy-sql -file .\imports\old-elfdict.sql -source neologism -db data/elfgloss.db
go run ./cmd/elfgloss audit -db data/elfgloss.db
go run ./cmd/elfgloss audit -db data/elfgloss.db -require-all-sources
go run ./cmd/elfgloss audit-code -root ..
go run ./cmd/elfgloss release-report `
-db data/elfgloss.db `
-root .. `
-source-url https://example.com/your-fork-source `
-source-review-file .\imports\source-review.json `
-legacy-sql .\imports\old-elfdict.sql `
-require-legacy-sql-comparison `
-require-runtime-integrationsThe host-only audit command fails if the local store contains unknown
dictionary sources, excluded community/status groups, stale indexes, source
entry-count mismatches, or entries attached to unregistered groups/languages.
Add -require-all-sources for a release/build gate that also fails when any
retained required dictionary source, including the legacy-only Neologism and
Mellonath Daeron sources, has not been imported. In that mode audit also fails
when an imported source is missing one of its retained importable groups, such as
the two Eldamo neologism subsets, or when a retained source lacks source-level
upstream/import timestamp provenance or a completed import run with both an
upstream location and content hash.
New imports also reject excluded community/status groups before they are written
to the bbolt store.
The host-only catalog -coverage command proves the retained source catalog is
complete before any database exists: every importable group above must be covered
by a cataloged import path, while excluded UI/community groups must remain
uncovered. release-report includes the same coverage result.
The host-only audit-code command checks the source tree for removed PHP,
Laravel root artifacts, account, discussion, comment, feed, like, follow,
contribution, and web-admin surfaces. It also fails if public HTTP projections,
domain models, or store code grow account, user, or owner state. It is intended
as a pre-ship guard alongside the database audit.
The host-only release-report command combines strict database audit, code
audit, official elfdict.com/non-fork attribution, AGPL license metadata, fork
source URL validation, imported source/group/run metadata, public source
license, terms-review, a per-source import readiness table, legacy SQL
retained-row comparison, attribution metadata, preserved runtime integrations,
and host runtime readiness into one
release-readiness report. Use -require-legacy-sql-comparison when proving that
legacy-backed Neologism and Mellonath rows match an old ElfDict dump, and use
-require-runtime-integrations on public hosts that expose live Tengwar
transcription through Glaemscribe.
Database audit and code-audit failures are promoted into the top-level release
issue table with their source, group, entry, or file location. Unresolved source
terms or attribution reviews are release issues by default; use
-allow-source-review-warnings only for fixture and development checks.
For public serving, resolve those source-review issues with a host-only evidence
file passed as -source-review-file or ELFGLOSS_SOURCE_REVIEW_FILE. Generate a
current template from the host catalog with
elfgloss source-review-template -out imports/source-review.json; the checked-in
source-review.example.json shows
the file shape. Set publicServingApproved to true only after recording
reviewer, date, basis, and evidence URL or note for each reviewed source. The
recorded basis must match the catalog terms status: permission-required sources
need permission or authorization evidence, legacy-review-required sources need
legacy provenance or permission evidence, and review-required sources need
license, terms, redistribution, or permission evidence. The release gate rejects
the generated reviewer and evidence placeholders even if publicServingApproved
is flipped to true. Keep the completed file with deployment/release records
rather than exposing it through the public API.
The source-review file is for public dictionary sources only; do not add the
host-only elfdict-legacy-sources compatibility splitter as a reviewed public
source.
Run the local preflight before serving or merging backend changes:
cd backend
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\preflight.ps1On POSIX hosts, use sh scripts/preflight.sh from backend/. GitHub Actions
runs both scripts in
go-backend.yml: Ubuntu runs the shell
preflight, Windows runs the PowerShell preflight, and both exercise Go tests,
audit-code, sync-sourceui -check, refresh-sourceui -check, CLI build, catalog -coverage, integrations -check, local all-source fixture build through
build-data -manifest manifest-direct.json -legacy-sql legacy-retained.sql,
strict database audit through release-report, and a serve smoke test that
verifies strict startup audit, the served browser shell/assets, the configured
fork source URL, served /LICENSE and /NOTICE, read-only methods, legacy
read-only dictionary redirects, and fixture search. The source UI sync check
proves the Go-embedded static adapter output is current: index.html is
generated from the refreshed source views, app.js is the current native
adapter, and styles.css is compiled from the retained old SCSS through
backend/internal/httpapi/sourceui/upstream/adapter/sourceui.scss. The source
UI refresh check proves the pulled old TS/SCSS tree under
backend/internal/httpapi/sourceui/upstream/assets/ts and selected old Blade
views under backend/internal/httpapi/sourceui/upstream/views are reproducibly
refreshed from src/resources with account, discussion, game, admin, and other
community-specific surfaces pruned. The local direct fixture
manifest lives at
backend/testdata/ci-import/manifest-direct.json
and is tested as coverage for every direct-upstream catalog source. The legacy
fixture fills the retained legacy-SQL-backed sources without network access.
Preflight uses -allow-source-review-warnings so source-term review findings
stay visible without blocking offline fixture validation; public release
commands should omit that flag.
The workflow also has an opt-in manual live_upstream_smoke input for running
the live direct-upstream scripts on Linux and Windows. Push and pull-request CI
remain fixture-only and do not depend on the upstream source sites being online.
The live smoke also checks the requested search sample set:
hope,love,live,elf,flower,son,father,estel,heru,nasie.
The public GET /api/catalog endpoint exposes source-facing metadata only:
source keys, names, source URLs, license labels, compact terms status, terms
URLs, attribution, and import class. Imported source records returned by
GET /api/sources are projected into public-safe metadata: upstream version,
import date, and entry count are kept, while raw local import locations are
redacted. The browser source cards merge those imported records with the public
catalog, so a served build can show the actual upstream version, import date,
and entry count recorded in the database. Host rebuild details such as old PHP
import commands, legacy account-name filters, source-review notes, and the
full-dump compatibility importer stay in the host CLI/internal catalog. Public
source identity is separated from import provenance: stored homepageUrl values
normalize to the catalog source URL for known sources, while upstreamUrl
records the exact URL or local file that was imported. Public
entry and search responses also redact non-HTTP(S) external links and host-only
source-reference paths so local manifest, review, or dump paths do not leak
through entry metadata. Public
group endpoints always
omit excluded UI/community groups; the
-include-excluded flag exists only on host CLI commands.
GET /api/integrations exposes runtime/source attribution and browser-required
configuration only, such as Glaemscribe mode mappings, while host migration
notes and low-level endpoint templates remain internal.
GET /api/site exposes the fork marker, official elfdict.com URL, upstream
repository URL, configured fork source URL, AGPL license URL, source-required
flag, fork notice, and warranty notice.
The old read-only dictionary URLs /w/{word}/{language?} and
/e/{groupName}-{groupId}/{word}/{language?} are kept as compatibility
redirects into the new browser search. Legacy numeric gloss/version/external
group routes are not restored because they depend on old database IDs rather
than source-owned stable identifiers.
Importable dictionary sources currently encoded in Go:
- Eldamo
- Quettaparma Quenyallo
- Parviphith
- SINDICT
- Neologism
- Parma Eldalamberon 17 Sindarin Corpus
- Mellonath Daeron
- Tolkiendil Compound Sindarin Names
The preserved group list also includes Eldamo neologism/adaptations and Eldamo
neologism/reconstructions as Eldamo importer subsets. Any group, Subject of debate, and Verified and confirmed are intentionally excluded.
Importable source groups currently encoded in Go:
- Eldamo
- Eldamo - neologism/adaptations
- Eldamo - neologism/reconstructions
- Mellonath Daeron
- Neologism
- Parma Eldalamberon 17 Sindarin Corpus
- Parviphith
- Quettaparma Quenyallo
- SINDICT
- Tolkiendil Compound Sindarin Names
Excluded UI/community groups:
- Any group
- Subject of debate
- Verified and confirmed
GET /healthGET /GET /static/*GET /api/siteGET /api/sourcesGET /api/catalogGET /api/catalog/groupsGET /api/integrationsGET /api/inflections?language=quenya&word=autaGET /api/inflections?language=sindarin&word=carGET /api/transcriptions?mode=quenya&text=Elen+silaGET /api/groups?source=eldamoGET /api/languages?source=eldamoGET /api/search?q=elda&language=q&source=eldamo&group=eldamo-neologism-adaptations&limit=25GET /api/search?q=elda&language=QuenyaGET /api/suggestions?q=eld&language=q&source=eldamo&group=eldamo-neologism-adaptations&limit=10GET /api/entries/{id}GET /api/sources/{sourceKey}/entries/{sourceEntryId}GET /w/{word}/{language?}redirects to/?q=...GET /e/{groupName}-{groupId}/{word}/{language?}redirects to/?q=...&group=...when the group maps to a retained source group
The search language filter accepts compact source language IDs such as q and
s, plus old Parf display names such as Quenya, Sindarin,
North Sindarin, and Undetermined.
Entries belong to sources, not users. The new backend stores:
- sources
- import runs
- source groups
- languages
- lexical entries
- source references
The embedded store is bbolt, a pure-Go key/value database. Server mutation is done
by host CLI commands, not by web routes. elfgloss serve opens the built
database in read-only mode and requires the strict retained-source audit to pass
before listening, so the public server process cannot write import data or serve
a partial retained-source build, including a retained source imported with zero
entries, missing source provenance, or incomplete completed import-run metadata,
by accident.
Production builds through elfgloss build-data are atomic: imports and audits
run against a temporary bbolt file before the target database is replaced.
Runtime integrations and attribution are also preserved in Go metadata:
- Ungwe for Quenya verb inflections
- Rinor Sindarin Verb Conjugator
- Glaemscribe by Benjamin Babut for Tengwar transcription
- J.R.R. Tolkien artwork attribution for the old background paintings
Inflection lookup is implemented as read-only behavior through Ungwe for Quenya
and a Go port of the Rinor Sindarin conjugator:
elfgloss inflect -language quenya -word <word>,
elfgloss inflect -language sindarin -word <word>, and
GET /api/inflections?language=<quenya|sindarin>&word=<word>.
Tengwar transcription is exposed through a Go bridge to the official Glaemscribe CLI. Install the runtime on hosts that need live transcription:
gem install unicode_utils glaemscribe
go run ./cmd/elfgloss integrations -check
go run ./cmd/elfgloss release-report -db data/elfgloss.db -source-url https://example.com/your-fork-source -require-runtime-integrations
go run ./cmd/elfgloss transcribe -mode quenya -text "Elen sila"Use ELFGLOSS_GLAEMSCRIBE_COMMAND or -glaemscribe-bin when the executable is
installed under a non-default name or path; release-report uses the same
setting for runtime readiness, and serve -require-runtime-integrations uses it
as a startup gate before listening. Runtime readiness runs a small transcription
smoke test, so the command must be usable, not merely present on PATH. The
same bridge powers bounded public transcription: requests are capped at 8192
characters and each Glaemscribe command run has a 10-second timeout.
GET /api/transcriptions?mode=<mode>&text=<text>.
This repository is a fork and Go migration of Parf Edhellen / ElfDict. The official public Parf Edhellen service is elfdict.com. The upstream source repository is galadhremmin/Parf-Edhellen.
The upstream README identifies ElfDict / Parf Edhellen / elfdict.com as AGPL. This fork includes the GNU Affero General Public License v3.0 text in LICENSE and fork/source attribution in NOTICE.md.
For public hosting, publish the exact source URL for the deployed fork and pass it to the server. This must be the public source code for this fork, not https://www.elfdict.com/ and not the upstream https://github.com/galadhremmin/Parf-Edhellen repository:
go run ./cmd/elfgloss serve `
-db data/elfgloss.db `
-addr :8080 `
-source-url https://example.com/your-fork-source `
-source-review-file .\imports\source-review.json `
-require-runtime-integrationsAlternatively set ELFGLOSS_SOURCE_URL and ELFGLOSS_SOURCE_REVIEW_FILE.
Normal serving refuses to start without that source URL and without resolving
source-review blockers. The served page and GET /api/site expose the source
URL, and the footer also
links the AGPL text and states that the fork is provided without warranty. The
development-only -allow-missing-source-url flag can bypass the startup check;
in that mode the page displays a deployment warning instead of implying that the
upstream repository is this modified fork.
Source attributions, license labels, and terms-review status for dictionary
imports live in the source catalog:
backend/internal/sources/catalog.go.
The catalog distinguishes open licenses from sources that are technically
importable but need review or permission before public serving: Quettaparma
Quenyallo and Parviphith are review-required; Neologism and Mellonath Daeron
are legacy-review-required; Tolkiendil Compound Sindarin Names is
permission-required. Public source metadata is exposed through
GET /api/catalog and persisted onto imported source records exposed by
GET /api/sources; host-only review notes stay in the CLI/internal catalog.
Runtime integration attribution is exposed through GET /api/integrations
without host-only migration notes.
See docs/go-migration.md for the current migration boundary and the PHP features
that should not be ported. See docs/validation.md for verification gates,
including the live direct-upstream smoke and optional archived SINDICT dump
compatibility check. The live smoke is executable through
backend/scripts/live-direct-smoke.ps1 or backend/scripts/live-direct-smoke.sh
and verifies the current direct website/API imports without claiming the
legacy-only sources are present.