Changes from easydb 5
What changed between easydb 5 and fylr — the architecture, the migration path, and every known API-facing difference, by endpoint.
fylr is a complete rewrite of easydb 5. The server was rebuilt from scratch, but the API was deliberately kept compatible wherever possible: most API clients — including the easydb 5 web frontend, which fylr ships — continue to work without changes.
This page is aimed at developers and administrators who maintain API clients, plugins, or integrations, or who are planning a migration. It covers three areas:
Architecture — what changed under the hood
Upgrading from easydb 5 — the migration path and migration aids
API changes — every known API-facing difference, by endpoint
Architecture
Server and deployment
The fylr server is written in Go (easydb 5 was written in C++, with substantial Python components). fylr is a single binary that runs several services — api/webapp, backend and execserver — where easydb 5 needed a stack of separate components:
Runs natively on Linux, Windows and macOS.
Runs on Docker and Kubernetes; a docker-compose file and a Helm chart are available.
Apache and the separate easydb asset server (EAS) are no longer needed. fylr serves HTTP itself, and everything that involves external programs runs through the execserver, a service for executing jobs such as image scaling and metadata extraction. The execserver is part of the
fylrbinary; it runs in-process by default and can run standalone and be scaled to several instances.Several fylr instances can share one PostgreSQL and one OpenSearch behind a load balancer for horizontal scaling.
See Architecture for how the fylr components work together.
Database
fylr supports PostgreSQL as its database backend, plus SQLite, which is the out-of-the-box default and suited for development, testing and small installations. Since fylr no longer uses stored procedures or triggers (easydb 5 relied on both), additional backends can be added more easily.
The way data is stored has changed fundamentally:
All objects are stored as generic rows in a fixed set of tables: an
objectrow per object version, andvaluerows — one per stored field value — referencing it. The number of database tables is fixed and does not change with the user datamodel; a datamodel commit writes ordinary rows instead of running DDL. In easydb 5, every datamodel commit created, changed or dropped tables.Object history is stored in the same tables — historic versions are ordinary rows next to the current one. easydb 5 kept history in separate
:historyshadow tables filled by database triggers.The datamodel, localizations, and mappings are stored in the database as well — fylr no longer keeps any of them as files on disk.
Custom data (including external UUIDs) is stored once and referenced, instead of being copied into every cell as in easydb 5. As a consequence, the custom data type updater refreshes the shared datum instead of rewriting every object that uses it.
File storage
fylr stores file metadata in the database. Assets and previews can be stored on a traditional file system, on S3-compatible storage, or on Azure Blob storage.
Asset links are no longer served by Apache directly from disk — they are served through fylr and are therefore subject to rights management.
Files can also be referenced at a remote URL instead of being copied in (rput with leave=true); referenced originals can still produce renditions.
Search and indexing
fylr uses OpenSearch as its primary search index, but also supports Elasticsearch (as easydb 5 did). Note that GeoJSON search requires an OpenSearch index plus the
geo_supportcapability in the fylr license; Elasticsearch does not provide geo support in fylr.Events are indexed as well and can be searched through
/api/v1/search(search typeevent, requires the system rightsystem.api.event[get]). easydb 5 answered event queries from SQL only."Suggest" is backed by a term list in the database, updated on every object save — the periodic suggest index rebuilds of easydb 5 are gone. (A reindex does not recalculate the term list — see Term recalculation.)
Rights generation happens during normal object loading rather than in a separate database pass per objecttype after the search, so
generate_rights: trueno longer carries the extra cost it had in easydb 5.
File worker chain and the execserver
The file worker chain (formerly the easydb asset server) has been redesigned — see The File Worker:
The chain is configured in the base config. The worker system can employ any program to generate any version of any file. Besides the standard setup for images, videos, audio and office files (plus 3D gaussian-splatting previews), you can extend it for previews of your choice, such as CAD formats.
Everything that happens to files — format conversion, metadata extraction, OCR, IIIF tiling — is scriptable via YAML recipes.
Metadata extraction also runs through the execserver (fylr's own
metadatasubcommand, which in turn calls ExifTool; it can be wrapped and extended) and can be configured per file type.The original file can be replaced during upload: a produce-config version named
originalconverts the upload before it is stored.The exporter runs through the execserver as well, so external processors such as XSLT 3.0 (Saxon) can be used.
Renditions can also be produced on demand at download time and streamed without being stored.
Frontend and plugins
The web frontend is the same as in easydb 5, with a new design.
Frontend plugins are compatible with easydb 5. Server plugins must be adapted — see Plugin.
All plugin callbacks run through the execserver, so plugins can be written in any programming language. Server-side callbacks receive a token to call back into the API: browser-bound session tokens are replaced by short-lived unbound tokens that are revoked after the callback; tokens of regular API clients are passed through — see the callback contract. Hybrid plugins that serve easydb 5 and fylr from one codebase with shared Python code are possible (two manifests over the same sources).
Plugins can be enabled and disabled in the base config, and can be installed directly from the frontend.
Some plugins have moved into the core: OAI-PMH, the hotfolder, and LDAP authentication. WebDAV — in easydb 5 an Apache
mod_davshare used only to drop files into the hotfolder — is now served by fylr directly and supports both read and write;COPYis not supported and returns 405.
Authentication
Authentication has been rebuilt from scratch on top of OAuth 2.0 — for connecting software this is the biggest break with easydb 5. The easydb 5 login flow (fetch a session token via GET /api/v1/session, then POST /api/v1/session/authenticate with login and password, then pass token= with every request) no longer works: /api/v1/session is gone. Every API client must instead obtain an OAuth 2.0 access token — via the authorization code, password, or client credentials grant — and send it as Authorization: Bearer <token> (or ?access_token=). See OAuth 2.0 for the flows and API changes below.
fylr also acts as an OAuth 2.0 / OpenID Connect provider for third-party software (discovery under /.well-known/openid-configuration). Logging fylr in against an external identity provider is supported via SAML and LDAP; there is no OIDC client.
Export
Exports are generated in real time and no longer pre-produce their files to disk. When an export is started, only a list of files is computed; each file is generated on the fly when it is requested. Behavioral changes are listed under API changes → /api/v1/export.
New features
Capabilities in fylr that have no equivalent in easydb 5:
IIIF support built in (Presentation API 3.0; Image API 3.0 since fylr 6.34, 2.0 before). The tiler is configurable via the execserver — you can, for example, switch everything to JP2 or layered TIFFs. The integrated image zoom is mapped to the IIIF endpoint.
Polyhierarchies are supported — see Hierarchies and polyhierarchies.
Field inheritance in hierarchies: unset fields can inherit their value from the parent object, configured per mask field; the search/db output formats
long_inheritanceandfull_inheritanceinclude the inherited values.Automatic numbering, e.g. for building archival tectonics.
Inspect (
/inspect): a bare-metal database view of the structures and data in fylr, including a visual backup tool — see The /inspect backend.The supervisor: run and manage a fleet of fylr instances from one binary.
Upgrading from easydb 5
Upgrading from easydb 5 to fylr requires a complete backup and restore. The fylr binary provides a command line facility to export an easydb 5 (or fylr) instance and restore it into a fylr instance via the API — see the Migration Tool documentation.
Files are not copied into the backup by default — they are referenced by URL and fetched from the source instance during the restore, so the source must stay reachable until the restore has finished. --include-files (since fylr 6.34) packs them into the backup instead.
The API has gained several extensions that are useful for migrations:
UUIDs, system object IDs, and object IDs can be set externally (
_uuid,_system_object_id,_id— settable while the object is version 1, immutable afterwards).Linked objects can be "deferred" — a promise that a linked object will be migrated later. The link lookup sets
_allow_defer: trueand must address the target by_system_object_idor_uuid; the link resolves automatically once the target is saved.system.rootcan set_create_useron objects (see /api/v1/db).skip_constraintsrelaxes save-time checks — see /api/v1/db for the details and the further bulk-import parameters (skip_index,skip_plugins,skip_events).easydb 5 password hashes can be carried over —
fylr backupconverts them into fylr's hash formats. This is opt-in on both ends: the easydb 5 source must be configured to output password hashes, andfylr restoreneeds--include-password(default off) — see fylr restore.
API changes
Once a client has switched its login to OAuth 2.0, most of the API is unchanged: API clients — including the easydb web frontend — generally work as before. The differences are listed below.
Removed at a glance
/api/v1/session
OAuth 2.0 + /api/v1/user/session
/api/v1/session/change_password
/api/v1/user/change_password
XML export format easydb_flat
the value is still accepted as an alias and produces the standard easydb XML
format=xml on /api/v1/mask and /api/v1/schema (the easydb 5 default)
JSON; format=xml is silently ignored
search.highlight
—
Search type pool_management
—
Aggregation type asset
Type term with _linked._asset. field prefix
/api/v1/settings subpaths purgedata, restart, buildsuggest, updatecustomdata
—
Rights management
The
extensionsparameter of theasset_uploadright is gone. Onlyclassescan be configured; all extensions that the produce config assigns to a class are then allowed for upload.collection.BAG_CREATE[inherit_owner]is not supported.inherit_owneris always assumed: a new sub-collection without an owner inherits the parent's owner.The output of
_generated_rightsincludes more rights than in easydb 5: fylr reportsread,write,delete,acl,change_owner,unlink, plusowner. easydb 5 reportedwrite,delete,acl,change_owner,unlinkon objects (neverread) and onlyread,write,delete,unlinkon/db_info.
_standard rendering
fylr does not store _standard with the object data — it is rendered from the current object values and the mask configuration. (For search output it is prerendered when the object is indexed, so after mask or config changes search results can show the previous rendering until a reindex; /api/v1/db always renders it fresh.) Differences to easydb 5:
If
_standard.1has no data, the fallback#<system-object-id>is used, even if_standard.2or_standard.3is set. easydb 5 would not set_standard.1in that case.When rendering linked objects, the linked object's
_standard.1text is merged into the level configured on the link field; for the file and geo standards, the linked object's same-numbered level is merged into the parent's level.Links are followed to any depth (recursion into already-rendered objects is stopped, so cycles are safe).
Since fylr 6.10.2, the format configured on a value is always applied, regardless of the following value's format: a comma followed by square brackets renders
value1, [value2], where easydb 5 renderedvalue1 [value2]. Use the formatspaceto restore the old look.New standard options: geo fields can be placed into the standard (
standard_geo),nested_standard_first_onlyrenders only the first row of a nested table, andstop_if_setstops collecting further values for a level once one is set.
/api/v1/collection
Deep links have changed: for type collection, the secret is now the login and the collection UUID is the password (reversed from easydb 5); email users log in with email & user UUID instead of email & ACL UUID.
/api/v1/config
systemandpluginconfig are separated; there is a new object layerconfigwrapping each section.The config can be read filtered by path:
GET /api/v1/config/<path>.Partial updates are supported via
POST /api/v1/config/<path>— only the addressed path is patched. A plainPOST /api/v1/configstill replaces whole config sections, as in easydb 5.DELETE /api/v1/config/<path>resets the addressed part to its defaults.Reading the config no longer requires the
system.configright — items the session may not see are filtered out. Writing still requires it.New:
GET /api/v1/config/listreturns the config schema, including plugin-contributed entries.
/api/v1/db
Changes in format full:
Empty fields are included (easydb 5 omitted them).
_changelogcontains different fields than in easydb 5:batch_id,schema_versionandgroupsare gone,latest_versionis new, anduseris a full user object instead of a bare id. For versions, the changelog is complete (all versions up to the requested one).base_fields_onlyis not supported (in any mode).
Additional support:
"_version:auto_increment": true— a separate boolean key next to_version._create_user: setting it to a different user requiressystem.root(useful for migrations); setting it to yourself does not._create_useris included in the output when the mask has owner read enabled.skip_constraints(calledskipConstraintsbefore fylr 6.27.0): skips unique-key enforcement — violations are tagged withUNIQUE_KEY_VIOLATIONsystem tags instead of rejected — and permits objects without a pool, duplicate nested_uuidvalues, and multiple tags from one tag group.Further save parameters:
skip_index,skip_plugins,skip_events(the latter two requiresystem.root),dry_run, andawait_index(blocks until the request's own index jobs are visible).Tag lookups (
_tags: [{"lookup:_id": {"reference": ...}}]) accept only the keyreference; easydb 5 also accepted other reference columns.
/api/v1/db_info
/update:_available_tagsno longer includesis_default— with multiple pools, each pool defines its own tag defaults, so/createreports defaults per pool instead.The output of
_generated_rightsincludes more rights than in easydb 5 (see Rights management).Both
/createand/updateare pure permission probes — they never write.
/api/v1/eas
class_version_extensionis no longer indexed.Metadata groups have a new
valuenext toprint._not_allowedis not used; versions the user may not see are skipped instead.Formats:
shortno longer returns versions; the new formatstandarddoes — usestandardfor compatibility.standardis also the new default (easydb 5 defaulted tolong); the easydb 5 formatstatusis gone.POST /eas/putno longer supportsmapping— useGET /easinstead.GET /eas?mappingalways requiresobjecttype(andmask).skip_extension_checkwas replaced byproduce_versions=false. fylr has no upload extension allowlist;produce_versions=falsedisables rendition production for the upload./eas/produce: thetransformbody parameter is a single transform entry, no longer an array.New:
POST /eas/rput/bulkfor bulk remote uploads, and manual renditions —version_names+id_parentson put/rput upload a named version for an existing original.
/api/v1/event
URL parameter
base_typewas renamed tobasetype.POST /api/v1/eventis now symmetric: it receives the event as an object inside the top-level object'seventproperty, matching the response shape.DELETE /api/v1/event/list(with the same filter parameters asGET /api/v1/event/list) andDELETE /api/v1/event/<id>are new;DELETE /api/v1/event(URL and body POST) was removed.URL parameter
background=1writes the event in the background. Use this from within plugins that are called during a write transaction to avoid deadlocks on SQLite installations.New:
POST /api/v1/event/listcreates multiple events in one call, andGET /api/v1/event/streamstreams pollable events over a WebSocket.skip_constraints=1(requiressystem.root) unlocks writing system event types and setting the event user — added for migrations.
/api/v1/export
Exports are generated on the fly (see Export above). Details:
POST /export/<id>/stopworks as in easydb 5; the stopped export ends in statusfailed. (It was a no-op in fylr before 6.27.0.)The XML mapping value
easydb_flatis still accepted as an alias but produces the standard easydb XML — a different structure than easydb 5's dump of its internal tables.In asset export attributes,
JSONandEASParentIdhave no effect.New URL parameter
formatonGET /export/<id>:long(default) returns all of the export's events in_log,standardreturns only the last run'sEXPORT_*events,shortomits_log._logis now sorted ascending.CSV: the subfield separator changed from
#to..CSV: the field syntax changed; output into a combined nested-collection cell using
[]is no longer possible — each row gets its ownfield[0],field[1], … column.CSV: the option
empty_columnsis no longer supported.FILE_DOWNLOAD,FILE_DOWNLOAD_ERRORandOBJECT_DOWNLOADare new events (easydb 5 usedASSET_DOWNLOADetc.).event.infocan be passed via URL for additional log entries in the event'sinfofield.Mapping can be used with
xml_one_file_per_object=false, and mapping can be combined with XSLT processing.New: XLSX as an export format, and
tar_gzas a packaged download next tozip.
/api/v1/group
_ipv4_subnet_filterwas renamed to_ip_subnet_filter; IPv6 is now supported. Saving a group with an unparseable filter entry is rejected (InvalidSubnetFilter).New:
_ip_subnet_filter_excludeinverts the filter — matching networks are excluded instead of required.
/api/v1/l10n
Writing the datamodel localization (
POST /l10n/user/HEAD) requiressystem.datamodelat leveldevelopmentorcommit(easydb 5: leveldevelopment).easydb 5's
POST /l10n/user/CURRENTpath is gone; fylr only exposesHEAD.
/api/v1/mask
format=xmlis no longer supported (it was the easydb 5 default); responses are always JSON. New:format=longembeds each field's underlyingcolumndefinition.The synthesized
_all_fieldsmask can be fetched viaGET /mask/<version>/_all_fields. It sets the text standard on the first text-type field, and the file standard (standard_eas, plusstandard_geo) on the first files field.In object output, the system fields
_acl,_tags,_owner, and_publishedare omitted when the mask hides them._collectionsis always included.
/api/v1/message
The message kind field was renamed from
type(values such aseula) towebfrontend_type, and the body frommessage_htmltomessage.
/api/v1/objects
columncan also target nested fields — but not linked or reverse-linked fields. Column-based deep links require the base config switchsystem.deep_link_access.allow_access_by_columnand error if the value matches multiple objects.
/api/v1/objecttype
New top-level attribute
_filename_replacements: lists the available replacements for files (output only).New top-level attribute
_compiled_tags; its entries carry_origin, showing where each tag comes from (tags,ot:<id>:<name>, ortags-filledfor disabled filler entries).New:
GET /objecttype/<id>/stats.
/api/v1/pool
_compiled_tagshas a new attribute_origin, which shows the origin of each tag (tags,objecttype:<id>:<name>,pool:<id>, ortags-filledfor disabled filler entries).
/api/v1/publish
easydb 5 documents this endpoint as insert-only; fylr additionally updates the existing row when a descriptor's
_idis set.
/api/v1/right/{context}/presets
New: a single preset can be fetched by ID —
GET /right/<context>/presets/<id>.
/api/v1/schema
format=xmlis no longer supported (it was the easydb 5 default); the graphical formatssvgandpngremain, JSON is the default.
/api/v1/search
fields.with_pathis new, and collecting fields for path items has changed: each entry is an individual array with one element per path element, whereas easydb 5 returned all path elements in one big array.sort.with_pathdefaults tofalse(easydb 5:true).include_deletedis a new switch that also searches deleted objects.changelog_rangeacceptsqueryto set the type of the comment search.New search type
event: searches events (requires the system rightsystem.api.event[get]). The keys of an event'sinfoblock are searchable and aggregatable viaevent.info_key; theinfovalues themselves are not indexed.Removed:
class_version_extension,class_version_filesize, and the asset-level date fieldsdate_createdanddate_inserted. The capture date now lives intechnical_metadata.create_date(plus the newtechnical_metadata.date_time_original), and each file field offers the derived daterangebest_datenext todate_uploaded.The search type
pool_managementwas removed._linked._assetsupports the same fields as regular file fields, and_linked._assetfields (such as date fields) are available in regular aggregations.Aggregation type
assetwas removed. Use typetermwith_linked._asset.as the field name prefix.search.highlightwas removed.The
matchsearch type was renamed totext;matchis kept as a compatibility alias.New subsearch feature for type
in: usefieldfor the field to fetch andsubsearchfor a subsearch that fills theinparameter.Type
rangehas new optionsfrom_equalsandto_equals(both default totrue, i.e. inclusive bounds).New output formats:
standard_extended,long_inheritance,full_inheritance.New
point_in_timeandsearch_afterfeatures for stable pagination.languagewas changed tolanguages(an array).The
linked_objectfacet now includes_linked_object.formatwas removed from thedate_rangeaggregation.New sort fields
_standard_parentand_standard_parents.New geo support: search element types
geo_bounding_boxandgeo_shape, and aggregation typesgeo_bounds,geotile_gridandgeohash_grid.New top-level parameters:
timezoneandfile_url_expire.
/api/v1/session
This endpoint was removed — the easydb 5 login flow (GET /session + POST /session/authenticate + token=) does not work against fylr. Clients log in via OAuth 2.0 and send the access token as Authorization: Bearer (or ?access_token=); GET /api/v1/user/session returns the session for a token. See Authentication.
/api/v1/settings
GET /api/v1/settings itself still exists for compatibility, but all subpaths are gone: purgeall, reindex and sendmail moved to /api/v1/system; purgedata, restart, buildsuggest and updatecustomdata were dropped without replacement.
/api/v1/system
New endpoint offering:
POST /purgeall: purge all data and start over;set_passwordsets the root password of the wiped instance. Requiresallowpurgein bothfylr.ymland the base config, and the actual root user.POST /reindex: initiate a reindex;blockFrontend=1blocks the frontend while it runs. Like easydb 5'ssettings/reindexit requiressystem.root, but no enable flag.POST /sendmailand/sendmail/test.PUT /backup/new,GET|DELETE /backup/<id>,GET /backup/<id>/download,GET /backup/list: backups, performed in the background (require thesystem.backupright).GET /errortest: store a test error.GET /status: status information about the server.GET /openapi/spec.json: the OpenAPI spec, plus storage location and share-link management routes.
/api/v1/tags
Authentication and rights failures return 401/403 (easydb 5 returned 400 for both).
Delete confirmations work via
delete_policyand theTagsInUseerror; easydb 5'scollection_rights_policyparameter is gone.
/api/v1/transitions
Both
GETandPOSTnow require the system rightsystem.tagmanager(easydb 5 allowedGETfor any session and gated only the save).Failures return 401/403 (easydb 5 returned 400).
/api/v1/user
The base config option
default_delete_policydefaults toask(easydb 5 had no delete policy for users).New endpoint
/api/v1/user/change_password(moved from the former session endpoint). It requires thesystem.user.change_passwordright, takesnew_password+password(the current password), and invalidates the user's tokens on success.include_password=1returns_password_hashas a singlemethod:hashstring (writable withsystem.root); easydb 5 returnedpassword_hash_methodand a separate salt. Both systems requiresystem.rootto read it.GET /api/v1/user/sessionreturns the current session for the token.Renamed:
user.created_timestamp→_created_at,user.last_updated_timestamp→_updated_at, and the list parametergroupids→group_ids._last_seen_atis always included when set; the easydb 5 parameterinclude_last_seen=1(root only) is gone.
OAI-PMH
OAI-PMH moved from a plugin into the core.
New endpoint
/api/v1/oai(easydb 5:/api/v1/plugin/base/oai/oai). Anonymous requests run as the system usersystem:oai_pmh.XSLT is applied once to the combined easydb XML of all records in the response, not to individual records.
The easydb XML elements are no longer prefixed with
easydb:. Instead,xmlnsis set on the top-level easydb XML element.Collection sets are now reported in each record's header. Set names have changed:
tagfilter:<name>becametagset:<name>,objecttype_pool:<ot>:pool:<ids>becameobjecttype:<ot>:pool:<ids>. The collection sets reported for each record are not filtered by the user's rights.Non-spec
limitURL parameter. It defaults to the base config'srecords_limitand may override it, up to a hard cap of 10000.
Custom data type updater
The easydb 5 request fields
plugin_configandserver_configare gone — use the%info.json%command line replacement instead; itsconfigkey holds the compiled plugin config.Updater scripts can return event log information:
logentries are added to theCUSTOM_DATA_TYPE_UPDATERevent'sinfo.log.New action
end_update(easydb 5 had onlystart_updateandupdate).The updater de-duplicates returned custom data by UUID: duplicate datums are merged and the affected objects reindexed.
Last updated