All notable changes to the Synthigy engine. Releases before 0.3.0 are described in their GitHub release notes.
SYNTHIGY_SERVER_TRUST_PROXY lists your proxies. It takes IPs or CIDRs,
comma separated — 127.0.0.1/32,::1/128 for a proxy on the same host —
instead of true. X-Forwarded-For is believed only from those addresses
and is read from the right, skipping trusted hops, so a proxy may overwrite
or append to the header. true is ignored with a warning: until you change
it, every request is recorded with the proxy's address. See
PROXY.md.
X-Forwarded-Host and X-Forwarded-Proto follow the same list. From
any caller not in SYNTHIGY_SERVER_TRUST_PROXY they are dropped before the
request is handled. With SYNTHIGY_IAM_ROOT_URL set nothing changes. Behind a
proxy, without TRUST_PROXY and without ROOT_URL, the token issuer and
login links now take the scheme of the connection (http) and the host from
Host — set one of the two. A directly exposed engine still believes
Host; only SYNTHIGY_IAM_ROOT_URL pins it.
UserInfo requires the openid scope. A token without it — every
client_credentials token — gets 403 insufficient_scope (OIDC Core §5.3,
RFC 6750 §3.1); it used to return the service user's claims. synthigy whoami on a service profile needs the portal release that ships with this
engine.
One system dataset: Synthigy. The IAM dataset is renamed Synthigy and
absorbs the Datasets meta-model; a fresh install deploys Synthigy and OAuth.
On an engine running IAM, an upgrade deploys Synthigy and retires the
Datasets dataset (its rows only — no table or column is dropped). The
retire is skipped while Synthigy does not cover everything Datasets owns
(datasets-retire-skipped at warn, retried every boot) and never blocks
boot. The schema drift stamp loses its Datasets key and Synthigy's changes,
so synthigy schema check and SDK codegen report the schema stale once —
pull it again. A fresh install that starts only the dataset module gets the
user, user_group and user_role tables too.
Synthigy and OAuth change only through engine upgrades. For anyone but
a superuser, a deploy, recall or destroy that targets either dataset is
refused (SYSTEM_DATASET_ENGINE_OWNED), and so is one whose result would
change a system entity or relation — a user dataset carrying a copy of
User, User Group, User Role or an OAuth entity that differs from the one in
force (SYSTEM_ENTITY_ALTERED; the error lists the changes). A superuser's
deploy goes through and the changes are logged at warn
(:synthigy.engine/deploy-warning). Such a dataset deploys again once its
copies match the global model (Pull or Revert in the modeler's deploy
drawer).
RLS on system entities comes only from Synthigy and OAuth. Guards that
another dataset declares on User, User Group, User Role or an OAuth entity
are ignored, for every role — they used to apply engine-wide, in the
console too. On upgrade such guards stop filtering, and the next deploy of
that dataset is refused with SYSTEM_ENTITY_RLS until they are removed. Put
guards on your own entities instead.
Dataset meta-model rows are written by the engine only. Through /data:
ENGINE_OWNED). A role grant that names them by id keeps working.deployed and deployed_on are set only by deploy and
recall. For anyone but a superuser, a deployed version cannot be changed,
moved to another dataset, unlinked from its dataset or deleted with a
plain delete (IMMUTABLE_VERSION_ALTERED); recall and destroy remove
them.VERSION_NAME_TAKEN (the
details say whether the holder is deployed). Duplicates already in the
database stay; upgrades only warn about them.purge whose selection reaches a meta-model entity is refused
(FORBIDDEN_OP); slice across a meta-model link is refused
(ENGINE_OWNED), and across Dataset → Versions for anyone but a
superuser.deploy needs the dataset's id. The payload's dataset must carry an
xid or euuid; a name alone is refused with BAD_OP. Only the version's
ids, name, configuration, model and the dataset reference are written
— anything nested under dataset is ignored.
Clojure API: IAM model names are Synthigy names. For embedders and
library callers: :iam/id is :synthigy/id (same xid);
synthigy.iam/current-version and register-reference-types! are
synthigy.dataset/current-synthigy-version and
synthigy.dataset/register-reference-types!; the patch topic
:synthigy.iam/model is :synthigy/model; dataset/iam.json is
dataset/synthigy.json. Bootstrap log ids changed with them
(:synthigy-model-loading, :synthigy-model-mounted,
:synthigy.iam/deploying-synthigy-model). retire-datasets!,
mount-synthigy-schema!, fix-deployed-on!, model-ids and
can-migrate-to-xid? moved from synthigy.dataset to
synthigy.dataset.patch, and the :synthigy/dataset upgrade steps with
them.
Audit history is no longer tenant-scoped. New history rows and live
subscription events carry no tenant (tenant: null); /history no longer
forces one, so a tenant a caller passes now filters — and matches only
rows written before this release.
Every log line knows where its request came from. Request-scoped lines
carry the client address in a new, indexed ip column — filter with
{:where {:ip "198.51.100.7"}} or :group-by :ip — and ctx carries the
proxy's address (peer), the user agent and the raw X-Forwarded-For.
Existing log tables gain the column on first boot.
Log lines name the session and the token. A request with a bearer token
carries the OAuth client, the token's jti and the first six characters of
its session id in ctx; a request with only the session cookie carries the
session. Issued access tokens are logged at info with their jti, so a
token can be followed from the address it was issued to every address that
uses it. The full session id is never logged.
Refused tokens are logged. A bearer token refused on /data, /logs,
/history, subscriptions, SSE, /oauth/userinfo or /oauth/password
leaves one token-rejected line at warn
with the reason — forged, expired, minted for another audience, for a
disabled client, for a deactivated user or for a user that no longer
exists — and the address. When the signature holds, the line also names
the token's jti, client and user, and when an expired token expired.
Successful writes are logged. /data writes log at info like reads: one
line per operation, with its source address. The audit trail keeps the
values; the log line says who and from where. Expect more log lines.
A request id from an untrusted caller is kept aside. An X-Request-Id
sent by a trusted proxy becomes the request's id; from anyone else it is
recorded as client-request-id and a fresh id is issued. The response's
X-Request-Id always names the id in use.
Every deploy logs what it changes — the same diff the modeler's deploy
drawer shows (:synthigy.dataset/deploy-changes), including the deploys a
system upgrade runs on its own.
GET /health for load balancers and supervisors. Unauthenticated, no
cache: 200 {"status":"ok"}, 200 "degraded" while audit or logs lag
behind, 503 "unavailable" when the database does not answer within 2 s or
a required module is down. The body names each check. See
OPERATIONS.md.
OIDC login_hint pre-fills the sign-in. The built-in login page fills
the username and focuses the password; a custom login page gets it as
?login_hint= next to state (trimmed, at most 256 characters). See
LOGIN_PAGES.md.
Console: one-time sign-in link for a user. The user page can generate a link, valid 24 hours, where the person sets a password or links an identity provider. It needs a fresh sign-in, is shown once, cancels the previous link, and is offered only to an admin of that user — never for an app's service user.
Custom account set-up page. A claim.html in the default custom-page
bundle replaces the built-in page a sign-in link opens. It receives
?token=, methods (only when the link restricts them) and error
(password_too_short), and posts {token, password} to
/oauth/claim/password. A rejected password does not use up the link.
App URL on apps. Where people open the app. A sign-in link made for
an app (console picker, or /oauth/onboard without return_url) sends the
person there after they set up their account; without an app it ends on
the status page.
deploy-check asks before deploying. Same scope as deploy
(dataset:deploy); returns {errors, warnings} — each shaped like a /data
error with code, message, hint and details — for the version given,
without deploying it. A deploy throws the first of those errors. The
modeler's deploy drawer calls it as you edit.
synthigy-datasets names the engine's own datasets — the ids of
Synthigy and OAuth, scope dataset:load.
Model subscriptions announce a running change. Besides
{"action": "deploy"}, model subscribers get {"action": "busy", "busy": {action, dataset, version, by, since}} when a deploy, recall or destroy
starts and {"action": "idle"} when it ends. A client that refetches on
every model event now refetches twice more per deploy.
PROXY.md: behind an AWS Application Load Balancer. ALB settings, and nginx, Caddy and Traefik configs that keep the client address the ALB recorded instead of replacing it with the ALB's. See PROXY.md.
One model change at a time. A deploy, recall or destroy started while
another runs is refused at once with MODEL_BUSY, naming the change in
progress, who started it and when. Try again when it finishes. The deploy
checks run inside the same lock, against the model the change applies to.
Deploys refuse new name collisions with a code. Two active entities
whose names map to the same table were refused as OPERATION_ERROR; now
ENTITY_NAME_CONFLICT. New: an attribute and a relation label (or two
labels) that give an entity the same field key are refused with
FIELD_KEY_CONFLICT. Only collisions the global model does not already have
count. Renaming a deployed version through deploy is refused like any
other change to it (IMMUTABLE_VERSION_ALTERED).
System models: Synthigy 0.90.1, OAuth 0.2.10. Their copies of the shared
entities are now identical: Name is unique and mandatory on User, User
Group, User Role, Dataset and Dataset Entity, and User Password is
classified secret in both. Before, OAuth's copy won on a fresh install and
the password was not marked secret. Existing installs deploy both on
upgrade; no NOT NULL is added, so rows without a name do not block it.
A bearer token's signature is checked once a minute, not on every
request. Verified claims are kept for 60 seconds per token while its
signing key is still loaded; rotating a key away invalidates them at once.
Expiry, audience, client and user are still checked on every request. In a
benchmark (enso, Postgres) /data reads went from about 23k to 31k requests
per second.
deployed-model and runtime-model return the full model to a token with
schema:read, as schema already did. Without it they return what the
caller may read.
Facebook sign-in uses Graph API v26.0 and sends appsecret_proof, so it
works with Meta's "Require App Secret" setting on. The console's Facebook
guide now walks through developer registration, the email permission and
publishing the app.
Server adapters: http-kit, enso and Jetty. :server-enso
(enso, virtual threads, HTTP/1.1, h2, h3)
is new; http-kit stays the default and is the only adapter in the
published bundles. :server-pedestal and
:server-undertow are removed. patcho now comes from Clojars
(dev.gersak/patcho 0.4.4) instead of a submodule.
The console is for administrators only. Signing in, and every console page, needs a System Operator role or the right to change users, groups, roles, apps, APIs, providers or connectors. Other accounts are told to manage their account in their app. Profile, Sessions and Sign-in methods go with it.
Console: "Login pages" is now "Custom pages", at
/console/custom-pages (was /console/login-pages). The page documents
claim.html beside the other bundle files.
Upgrading from the Datasets meta-model is one step. The legacy
:synthigy.dataset/model 1.0.0–1.0.6 upgrades are replaced by a single
1.0.6 step that brings the tables in line with Synthigy without writing a
version row, then backfills deployed_on where it is empty.
dataset/dataset.json is no longer shipped.
The built-in login page keeps the username after a failed sign-in and
lets password managers fill saved credentials (autocomplete was
new-password). Errors show in an alert box with shorter copy ("Wrong
username or password."); the page is restyled.
Custom login pages reusing /oauth/css/login.css lose the span.error
rule — errors are styled by .sy-error-slot / .sy-error now. A page that
shows errors in <span class="error"> renders them unstyled until it
switches.
The docs and the console say synthigy portal where they said
synthigy console; the portal keeps console as an alias.
The console's bundled tools (modeler, data console, log cockpit) are
rebuilt: modeler notifications stay bottom-right over an open drawer, and
the login-hint attribute is gone (name a demo account on the instance's
login page instead).
SELECT (or VALUES), and the template runs in a read-only
transaction. A template that wrote now fails with QUERY_NOT_SELECT or a
database error.SYNTHIGY_ID_FORMAT=euuid the engine's delete hooks never
fired: a delete of a Dataset Version was a plain row delete, with no recall
and no checks._order_by on a nested relation in the [[field, direction]] form,
which XSQL's order by produces, failed with INTERNAL_ERROR.duplicate column name — the column is reused, as on Postgres.request_id on audit rows was always
empty. It now carries the request's id, which joins an audit row to the log
lines of the request that made the change; history timelines grouped by
request work.X-Forwarded-For entry from any caller. The engine now sees the connection's
own address (http-kit 2.9.0-beta3) and decides the client address in one
place for every server adapter; sessions and login checks use it.[:data :elapsed-ms]
or [:ctx :user-agent] never matched on the SQLite, DuckDB and ClickHouse
log stores.login-authenticated and
login-credentials-rejected logged client: null.error dropped the record of
the change itself. config-changed and config-cleared are now written at
warn before the change applies, whatever the levels, and name the store
level too.WWW-Authenticate challenge. /data, /logs,
/history, subscriptions and SSE answered 401 without the header RFC 6750
requires. They now send Bearer, or Bearer error="invalid_token" when a
token was refused./oauth/password took a
token for any audience from any client — a token minted for a partner API
could change its user's password. It now requires the platform or identity
audience, an active client and an active user.client_credentials token introspected as {"active": false}, because only
tokens with a stored session row counted. It is now active while it is
unexpired and its client and service user are active (RFC 7662 §2.2)./oauth/userinfo returned the
claims of a deactivated user, and nulls for a user that no longer exists. Both
are now refused with invalid_token. A failure on the server's side is a 500
server_error and is logged; it used to be a silent 403.Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |