pgkiln 0.37.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/SECURITY.md +3 -0
  2. package/db/migrations/091_workspace_service.sql +20 -0
  3. package/db/migrations/092_app_groups.sql +23 -0
  4. package/db/migrations/093_workspace_preferences.sql +34 -0
  5. package/db/migrations/094_task_vacation_expiry.sql +182 -0
  6. package/docs/apex-feature-parity.md +16 -11
  7. package/docs/guide/03-builder.md +119 -6
  8. package/docs/guide/04-pages-and-regions.md +21 -5
  9. package/docs/guide/06-processing.md +27 -1
  10. package/docs/guide/12-development.md +16 -4
  11. package/docs/guide/13-rest-api.md +3 -1
  12. package/examples/gallery/README.md +13 -0
  13. package/examples/gallery/gallery.json +20 -0
  14. package/examples/gallery/starters/asset-inventory.json +30 -0
  15. package/examples/gallery/starters/contacts.json +41 -0
  16. package/examples/gallery/starters/event-registrations.json +29 -0
  17. package/examples/gallery/starters/expense-claims.json +32 -0
  18. package/examples/gallery/starters/team-tasks.json +35 -0
  19. package/examples/hr/hr_56_more_charts.sql +57 -0
  20. package/examples/hr/hr_57_task_policies.sql +18 -0
  21. package/package.json +1 -1
  22. package/public/app.css +25 -0
  23. package/public/builder.css +91 -0
  24. package/public/sw.js +2 -1
  25. package/src/app.ts +8 -0
  26. package/src/automations.ts +6 -0
  27. package/src/builder/app-groups.ts +132 -0
  28. package/src/builder/blueprints.ts +14 -0
  29. package/src/builder/components.ts +7 -0
  30. package/src/builder/cross-app.ts +106 -0
  31. package/src/builder/gallery.ts +154 -0
  32. package/src/builder/home.ts +30 -10
  33. package/src/builder/region-settings.ts +3 -0
  34. package/src/builder/routes.ts +76 -24
  35. package/src/builder/scripts.ts +6 -2
  36. package/src/builder/sql-utilities.ts +278 -0
  37. package/src/builder/sql.ts +108 -7
  38. package/src/builder/ui.ts +45 -9
  39. package/src/builder/workspace-backups.ts +78 -0
  40. package/src/builder/workspaces.ts +198 -7
  41. package/src/cli/replace.ts +4 -0
  42. package/src/i18n/ar.ts +25 -0
  43. package/src/i18n/cs.ts +25 -0
  44. package/src/i18n/da.ts +25 -0
  45. package/src/i18n/de.ts +25 -0
  46. package/src/i18n/el.ts +25 -0
  47. package/src/i18n/es.ts +25 -0
  48. package/src/i18n/fi.ts +25 -0
  49. package/src/i18n/fr.ts +25 -0
  50. package/src/i18n/he.ts +25 -0
  51. package/src/i18n/it.ts +25 -0
  52. package/src/i18n/ja.ts +25 -0
  53. package/src/i18n/ko.ts +25 -0
  54. package/src/i18n/nb.ts +25 -0
  55. package/src/i18n/pl.ts +25 -0
  56. package/src/i18n/pt.ts +25 -0
  57. package/src/i18n/ru.ts +25 -0
  58. package/src/i18n/sv.ts +25 -0
  59. package/src/i18n/tr.ts +25 -0
  60. package/src/i18n/uk.ts +25 -0
  61. package/src/i18n/zh.ts +25 -0
  62. package/src/i18n.ts +50 -0
  63. package/src/runtime/calendar.ts +2 -1
  64. package/src/runtime/charts.ts +154 -5
  65. package/src/runtime/rest.ts +8 -2
  66. package/src/runtime/tasks.ts +62 -4
  67. package/src/session.ts +18 -2
  68. package/src/workflow.ts +6 -4
package/SECURITY.md CHANGED
@@ -69,6 +69,9 @@ first version (MVP). Every finding below has a regression test in
69
69
  | App Builder AI and blueprints (0.28.0) | Developer session and CSRF; only administrators choose the builder's AI service. Generated SQL is shown, never run automatically; page proposals are checked (page types, tables the app role can read, free page numbers) and created only after the developer confirms. The model sees names, types and keys, never rows. A blueprint is validated (identifiers, references, rows) and turned into quoted SQL; Create only accepts the exact text the review showed, signed with the developer's session, in one transaction. `meta.builder_ai`, `meta.ai_table_note` and `meta.blueprint` are closed to the runtime role. |
70
70
  | Workspaces (0.29.0) | Every builder request under `/apps/:id` or `/pages/:pid` (GET and POST), a working copy's main application, a boilerplate and the code editor's application are checked against the developer's workspaces in `developer()` and answer 404 outside them; lists are filtered by the current workspace. Switching needs CSRF and membership; managing workspaces needs an administrator and CSRF; names are escaped; subscriptions only reach theme and library applications of the same workspace. Workspace tables are closed to the runtime role. **A shared workspace is not a tenant boundary:** its SQL Workshop runs as the owner and its application code on the shared runtime connection; isolate a workspace for that (below). |
71
71
  | Isolated workspaces (sprint 60) | The boundary is the PostgreSQL login (`session_user`, which SQL can't change): an isolated workspace's applications run on its own run login and its developers' SQL on its own developer login (`src/workspace-db.ts`; passwords derived from `PGKILN_SECRET_KEY`, never stored), so `RESET ROLE` or `set_config('role', …)` only reach that login. The run login has `pgkiln_app_code` (the runtime role's rights in `meta`, kept in step after every migration by `meta.sync_app_code_grants()`, checked by a test) and the workspace's roles; the developer login only the workspace's roles. Row-level security on every `meta` table the runtime reads keys on the login (`meta.visible_apps()`), both ways: isolated workspaces don't see others, the shared runtime doesn't see isolated ones; the owner isn't affected. `meta.app_id()` and the eight functions that take an application id (`meta.checked_app`) refuse an application the login may not see. A workspace's database roles are an administrator's list (`meta.workspace_role`): an application there may only run as one of them, a role belongs to one workspace, and superusers, roles with CREATEROLE/BYPASSRLS/REPLICATION and pgkiln's own roles are refused; memberships follow by triggers (`meta.workspace_sync_roles()`). A new application gets a new role and a new or own schema. Every builder tool that runs a developer's SQL uses the workspace's developer login (`workshopDb`, `devDbForApp`); the import check plans the imported SQL on it, outside the import's owner transaction. Saved scripts, runs, blueprints and generators are per workspace. **Not covered:** administrators and developers who also belong to a shared workspace (owner rights there), the `pg_catalog` (definitions are readable by every login), PostgREST (one authenticator), identity providers, LDAP, mail and AI services (installation-wide). |
72
+ | Builder screens like APEX's (sprint 61) | The sign-in page's workspace is checked after the password (only the developer's workspaces; a wrong one answers like a wrong password, failures are throttled and logged as before); *Remember workspace and username* is an HttpOnly cookie for `/builder` holding no secret, read defensively and escaped; the redirect after sign-in is a `/builder` path only. Workspace message and banner: administrators and the workspace's administrators, CSRF, plain text escaped, the banner colour from a fixed list. Utilization: the same. SQL Workshop home: your own recent commands only (by id and username), the schema checked against the schemas the session's connection may use (and set as a parameter of `set_config`). Application groups: every query bound to the current workspace; groups and applications of other workspaces are ignored. App Gallery: keys are names from `examples/gallery/gallery.json`, never paths; sample apps (shipped SQL as the owner, demo accounts with known passwords) are for administrators only; starter apps go through the blueprint review and its signed create step. Cross application reports: fixed SQL per topic, the workspace id as a parameter. |
73
+ | Workspace preferences, extension links, Manage backups (sprint 62) | Preferences and links: administrators and the workspace's administrators, CSRF, logged. The SQL Workshop preference is checked in `developer()` for every `/builder/sql` GET and POST before a route runs, so no SQL Workshop route can skip it; it is not a sandbox (a developer of several workspaces uses it in another; isolation is the boundary). RESTful services off: the module lookup answers 404 before authentication or any handler SQL. Extension links: only `http://`/`https://` URLs without spaces, quotes or angle brackets (checked in Node and by a CHECK constraint), escaped, opened with `rel="noopener noreferrer"`. Manage backups: only the current workspace's applications, through the existing `meta.backup_app`. SQL Workshop utilities: fixed catalogue SQL chosen by a key from a fixed list (own-property lookup), run read only with a statement timeout on the SQL Workshop's connection (an isolated workspace's developer login sees only its roles' schemas); the Database Monitor shows other sessions' query texts, so it is for administrators only and cancels nothing. |
74
+ | Approval vacation rules and expiration (sprint 64) | A vacation rule is always the signed-in user's own (`meta.app_user()` in a security-definer function; no user field is read from the form); its table is closed to the runtime roles; the delegate's rights are computed in `meta.task_rights` on every call and only for tasks the user owns by name or is assigned (never role ownership), so they end with the rule. Dates are checked as dates; CSRF and an in-app `next`. The expiry tick runs as the owner from the scheduler only (no grant). |
72
75
  | Drawers, built-in template components, Theme Roller additions (0.29.0) | Dialog positions and sizes, base styles, dark-mode colours, item and report-column template options only reach the page from fixed lists (checked when saved and again when rendered: a hand-edited value is ignored); the browser gets dialog shapes as JSON values from those lists. Built-in template components pass the same template checks as plug-ins; *Copy into this application* takes a static id from the fixed list, needs CSRF and the application in the developer's workspace, and never overwrites the application's own component. |
73
76
  | Report row selection across pages (0.29.0) | `POST …/report/<id>/select` needs the CSRF token, page access and a visible report region whose `selection` names the item (and the item visible); it only changes that item's session state; values are bounded (5000, 400 characters, no `:`) and treated as user input, like a submit; output is escaped. |
74
77
  | Instance settings (0.29.0) | Administrators only, with CSRF; only whole numbers within each setting's range are stored (a bad stored value falls back to the environment variable or default); the configuration overview never shows secrets (connection strings, keys: only whether they are set); changes are logged.
@@ -0,0 +1,20 @@
1
+ -- =====================================================================
2
+ -- 091: a workspace's message and environment banner (APEX: Administration
3
+ -- → Manage Service → Define Workspace Message, Define Environment Banner)
4
+ --
5
+ -- message shown to the workspace's developers on the App Builder home
6
+ -- (plain text, at most 2000 characters)
7
+ -- banner_label a short label (e.g. DEVELOPMENT, TEST) shown in a coloured band
8
+ -- at the top of every builder page while the workspace is current
9
+ -- banner_color one colour of a fixed list (the builder's CSS draws them)
10
+ --
11
+ -- Administrators and the workspace's administrators set them.
12
+ -- Installation data, not exported.
13
+ -- =====================================================================
14
+ alter table meta.workspace
15
+ add column message text check (length(message) between 1 and 2000),
16
+ add column banner_label text check (banner_label ~ '^\S(.{0,38}\S)?$'),
17
+ add column banner_color text not null default 'blue'
18
+ check (banner_color in ('red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'gray'));
19
+ comment on column meta.workspace.message is 'Workspace message (091): shown to its developers on the App Builder home.';
20
+ comment on column meta.workspace.banner_label is 'Environment banner (091): a label shown at the top of the builder while the workspace is current.';
@@ -0,0 +1,23 @@
1
+ -- =====================================================================
2
+ -- 092: application groups (APEX: Workspace Utilities → Application Groups)
3
+ --
4
+ -- A workspace's named groups of applications, to organise the App Builder:
5
+ -- the home page filters and labels applications by group. They change
6
+ -- nothing at run time. Installation data (like workspaces): not exported,
7
+ -- kept by an in-place import.
8
+ -- =====================================================================
9
+ create table meta.app_group (
10
+ id serial primary key,
11
+ workspace_id int not null references meta.workspace on delete cascade,
12
+ name text not null check (name ~ '^\S(.{0,58}\S)?$'),
13
+ description text check (length(description) <= 2000),
14
+ unique (workspace_id, name)
15
+ );
16
+ create table meta.app_group_member (
17
+ app_id int primary key references meta.app on delete cascade,
18
+ group_id int not null references meta.app_group on delete cascade
19
+ );
20
+ create index app_group_member_group_idx on meta.app_group_member (group_id);
21
+ revoke all on meta.app_group, meta.app_group_member from public;
22
+ comment on table meta.app_group is 'Application groups (092): a workspace''s named groups of applications, for the App Builder. Not exported.';
23
+ comment on table meta.app_group_member is 'The group of an application (092); none: ungrouped.';
@@ -0,0 +1,34 @@
1
+ -- =====================================================================
2
+ -- 093: workspace preferences and extension links (APEX: Administration →
3
+ -- Manage Service → Set Workspace Preferences, Manage Extension Links)
4
+ --
5
+ -- sql_workshop false: the SQL Workshop (SQL Commands, Object Browser,
6
+ -- scripts, data loading, Quick SQL, query builder, DDL and
7
+ -- compare, AI) is refused while the workspace is current
8
+ -- rest_services false: the REST modules of the workspace's applications
9
+ -- answer 404 at run time (/a/<alias>/rest/...)
10
+ --
11
+ -- meta.workspace_link: links to other tools (monitoring, a wiki, a ticket
12
+ -- system) shown in the builder's Extensions menu while the workspace is current.
13
+ --
14
+ -- Administrators and the workspace's administrators set them.
15
+ -- Installation data, not exported.
16
+ -- =====================================================================
17
+ alter table meta.workspace
18
+ add column sql_workshop boolean not null default true,
19
+ add column rest_services boolean not null default true;
20
+ comment on column meta.workspace.sql_workshop is 'Workspace preference (093): false refuses the SQL Workshop while the workspace is current.';
21
+ comment on column meta.workspace.rest_services is 'Workspace preference (093): false turns off the REST modules of the workspace''s applications.';
22
+
23
+ create table meta.workspace_link (
24
+ id serial primary key,
25
+ workspace_id int not null references meta.workspace on delete cascade,
26
+ label text not null check (label ~ '^\S(.{0,58}\S)?$'),
27
+ url text not null check (url ~ '^https?://[^\s<>"]+$' and length(url) <= 2000),
28
+ description text check (length(description) <= 500),
29
+ position int not null default 0,
30
+ unique (workspace_id, label)
31
+ );
32
+ revoke all on meta.workspace_link from public;
33
+ comment on table meta.workspace_link is 'Extension links (093): a workspace''s links to other tools, in the builder''s Extensions menu. Not exported.';
34
+
@@ -0,0 +1,182 @@
1
+ -- =====================================================================
2
+ -- 094: approvals: vacation rules, and expiry, renewal and escalation of
3
+ -- overdue tasks (APEX: Task Definition → Vacation Rule, Expiration Policy)
4
+ --
5
+ -- Vacation rules: a user of an application names a delegate and a period
6
+ -- (meta.set_vacation_rule from the task list). While the rule is active the
7
+ -- delegate may act on the tasks the user may act on as a named owner
8
+ -- (owner_users) or as the actual owner, checked live in meta.task_rights:
9
+ -- when the rule ends, so does the delegate's access. Role owners are not
10
+ -- passed on: the role's other members are there for that.
11
+ --
12
+ -- Expiration policy per task definition (null: overdue tasks stay open):
13
+ -- expire an overdue task ends with state 'expired' (no outcome)
14
+ -- renew its due date moves on by "due in" (up to max_renewals times,
15
+ -- default 3), then it expires
16
+ -- escalate once: escalate_role becomes a potential owner, the priority 1,
17
+ -- the task unassigned with a new due date; overdue again: expired
18
+ -- meta.task_expire_tick() applies it (the server's scheduler, every minute).
19
+ -- A workflow waiting for a task that expires goes on with the outcome
20
+ -- "expired" (or ends, like a cancelled task, without an "expired" branch).
21
+ -- =====================================================================
22
+
23
+ alter table meta.task_definition
24
+ add column expiration text check (expiration in ('expire', 'renew', 'escalate')),
25
+ add column max_renewals int check (max_renewals between 1 and 100),
26
+ add column escalate_role text check (escalate_role ~ '^\S{1,100}$');
27
+ comment on column meta.task_definition.expiration is 'Expiration policy (094): expire, renew or escalate an overdue task; null: it stays open.';
28
+
29
+ alter table meta.task
30
+ add column renewals int not null default 0,
31
+ add column escalated_at timestamptz;
32
+ alter table meta.task drop constraint task_state_check;
33
+ alter table meta.task add constraint task_state_check check (state in ('unassigned', 'assigned', 'completed', 'cancelled', 'expired'));
34
+ alter table meta.task_event drop constraint task_event_event_check;
35
+ alter table meta.task_event add constraint task_event_event_check
36
+ check (event in ('created', 'claimed', 'released', 'delegated', 'approved', 'rejected', 'completed', 'cancelled', 'commented', 'closed',
37
+ 'expired', 'renewed', 'escalated'));
38
+
39
+ -- ---------------------------------------------------------------- vacation rules
40
+ create table meta.task_vacation (
41
+ app_id int not null references meta.app on delete cascade,
42
+ username text not null check (username = lower(username) and length(username) between 1 and 100),
43
+ delegate_to text not null check (delegate_to = lower(delegate_to) and length(delegate_to) between 1 and 100),
44
+ starts_at timestamptz not null,
45
+ ends_at timestamptz not null,
46
+ created_at timestamptz not null default now(),
47
+ primary key (app_id, username),
48
+ check (delegate_to <> username),
49
+ check (ends_at > starts_at)
50
+ );
51
+ create index on meta.task_vacation (app_id, delegate_to);
52
+ revoke all on meta.task_vacation from public;
53
+ comment on table meta.task_vacation is 'Vacation rules (094): while active, delegate_to acts on the user''s tasks. User data, not exported.';
54
+
55
+ /** The users (lower case) of this application whose active vacation rule names the current user. */
56
+ create function meta.acting_for() returns text[]
57
+ language sql stable security definer set search_path = meta, pg_catalog as $$
58
+ select coalesce(array_agg(v.username order by v.username), '{}')
59
+ from task_vacation v
60
+ where v.app_id = meta.app_id() and v.delegate_to = lower(meta.app_user()) and now() >= v.starts_at and now() < v.ends_at
61
+ $$;
62
+
63
+ create function meta.set_vacation_rule(p_delegate text, p_from timestamptz, p_until timestamptz) returns void
64
+ language plpgsql security definer set search_path = meta, pg_catalog as $$
65
+ declare
66
+ v_user text := lower(meta.app_user());
67
+ v_to text := lower(btrim(coalesce(p_delegate, '')));
68
+ v_from timestamptz := coalesce(p_from, now());
69
+ begin
70
+ if v_user is null or v_user = 'nobody' then
71
+ raise exception 'Sign in to set a vacation rule.';
72
+ end if;
73
+ if v_to = '' or length(v_to) > 100 or v_to ~ '\s' then
74
+ raise exception 'Name the user who takes over your tasks.';
75
+ end if;
76
+ if v_to = v_user then
77
+ raise exception 'You cannot delegate your tasks to yourself.';
78
+ end if;
79
+ if p_until is null or p_until <= v_from or p_until <= now() then
80
+ raise exception 'The vacation rule ends after it starts, and in the future.';
81
+ end if;
82
+ if p_until > v_from + interval '1 year' then
83
+ raise exception 'A vacation rule lasts at most a year.';
84
+ end if;
85
+ insert into task_vacation (app_id, username, delegate_to, starts_at, ends_at)
86
+ values (meta.app_id(), v_user, v_to, v_from, p_until)
87
+ on conflict (app_id, username) do update
88
+ set delegate_to = excluded.delegate_to, starts_at = excluded.starts_at, ends_at = excluded.ends_at, created_at = now();
89
+ end
90
+ $$;
91
+
92
+ create function meta.clear_vacation_rule() returns void
93
+ language sql security definer set search_path = meta, pg_catalog as $$
94
+ delete from task_vacation where app_id = meta.app_id() and username = lower(meta.app_user());
95
+ $$;
96
+
97
+ /** The current user's vacation rule in this application (none: no row). */
98
+ create function meta.vacation_rule() returns table (delegate_to text, starts_at timestamptz, ends_at timestamptz, active boolean)
99
+ language sql stable security definer set search_path = meta, pg_catalog as $$
100
+ select v.delegate_to, v.starts_at, v.ends_at, now() >= v.starts_at and now() < v.ends_at
101
+ from task_vacation v
102
+ where v.app_id = meta.app_id() and v.username = lower(meta.app_user()) and v.ends_at > now()
103
+ $$;
104
+
105
+ revoke all on function meta.acting_for(), meta.set_vacation_rule(text, timestamptz, timestamptz), meta.clear_vacation_rule(), meta.vacation_rule() from public;
106
+ grant execute on function meta.acting_for(), meta.set_vacation_rule(text, timestamptz, timestamptz), meta.clear_vacation_rule(), meta.vacation_rule() to public;
107
+
108
+ -- Same as 072, plus: the delegates of users on vacation act for them (named owners and actual owners).
109
+ create or replace function meta.task_rights(t meta.task) returns meta.task_rights
110
+ language plpgsql stable security definer set search_path = meta, pg_catalog as $$
111
+ declare
112
+ v_user text := lower(meta.app_user());
113
+ v_for text[] := meta.acting_for();
114
+ d meta.task_definition;
115
+ r meta.task_rights;
116
+ v_open boolean := t.state in ('unassigned', 'assigned');
117
+ v_mine boolean := lower(t.actual_owner) = v_user or lower(t.actual_owner) = any (v_for);
118
+ begin
119
+ select * into d from task_definition where id = t.definition_id;
120
+ r.is_initiator := lower(t.initiator) = v_user;
121
+ r.is_owner := exists (select 1 from unnest(t.owner_users) u where lower(u) = v_user or lower(u) = any (v_for))
122
+ or exists (select 1 from unnest(t.owner_roles) x where meta.has_role(x));
123
+ r.is_admin := d.admin_role is not null and meta.has_role(d.admin_role);
124
+ r.may_see := t.app_id = meta.app_id() and v_user <> 'nobody'
125
+ and t.tenant_id is not distinct from meta.tenant_id()
126
+ and (r.is_initiator or r.is_owner or r.is_admin or coalesce(v_mine, false));
127
+ -- an initiator may not decide on their own request (unless the definition allows it)
128
+ r.may_act := r.may_see and v_open and (d.initiator_can_complete or not r.is_initiator)
129
+ and (coalesce(v_mine, false) or (t.state = 'unassigned' and r.is_owner));
130
+ r.may_claim := r.may_see and t.state = 'unassigned' and r.is_owner and (d.initiator_can_complete or not r.is_initiator);
131
+ r.may_release := r.may_see and t.state = 'assigned' and coalesce(v_mine, false);
132
+ r.may_delegate := r.may_see and v_open and (coalesce(v_mine, false) or r.is_admin);
133
+ r.may_cancel := r.may_see and v_open and (r.is_initiator or r.is_admin);
134
+ return r;
135
+ end
136
+ $$;
137
+
138
+ -- ---------------------------------------------------------------- expiration
139
+ create function meta.task_expire_tick() returns int
140
+ language plpgsql security definer set search_path = meta, pg_catalog as $$
141
+ declare
142
+ x record;
143
+ n int := 0;
144
+ v_due interval;
145
+ begin
146
+ for x in
147
+ select t.id, t.due_at, t.renewals, t.escalated_at, t.owner_roles, d.expiration, d.max_renewals, d.escalate_role, d.due_in
148
+ from task t join task_definition d on d.id = t.definition_id
149
+ where t.state in ('unassigned', 'assigned') and t.due_at < now() and d.expiration is not null
150
+ order by t.due_at
151
+ limit 500
152
+ for update of t skip locked
153
+ loop
154
+ v_due := case when x.due_in is not null then x.due_in::interval end;
155
+ if x.expiration = 'renew' and v_due is not null and x.renewals < coalesce(x.max_renewals, 3) then
156
+ update task set due_at = greatest(x.due_at + v_due, now() + v_due), renewals = renewals + 1 where id = x.id;
157
+ insert into task_event (task_id, username, event, detail)
158
+ values (x.id, 'system', 'renewed', format('%s of %s', x.renewals + 1, coalesce(x.max_renewals, 3)));
159
+ elsif x.expiration = 'escalate' and x.escalated_at is null then
160
+ update task
161
+ set owner_roles = case when x.escalate_role is null or x.escalate_role = any (owner_roles) then owner_roles
162
+ else array_append(owner_roles, lower(x.escalate_role)) end,
163
+ priority = 1, state = 'unassigned', actual_owner = null, escalated_at = now(),
164
+ due_at = case when v_due is not null then now() + v_due end
165
+ where id = x.id;
166
+ insert into task_event (task_id, username, event, detail) values (x.id, 'system', 'escalated', x.escalate_role);
167
+ else
168
+ update task set state = 'expired', completed_at = now() where id = x.id;
169
+ insert into task_event (task_id, username, event) values (x.id, 'system', 'expired');
170
+ end if;
171
+ n := n + 1;
172
+ end loop;
173
+ return n;
174
+ end
175
+ $$;
176
+ revoke all on function meta.task_expire_tick() from public;
177
+
178
+ -- Same as 022, plus: an expired task wakes its workflow too.
179
+ drop trigger task_wakes_workflow on meta.task;
180
+ create trigger task_wakes_workflow after update of state on meta.task
181
+ for each row when (new.workflow_id is not null and new.state in ('completed', 'cancelled', 'expired') and old.state is distinct from new.state)
182
+ execute function meta.task_wakes_workflow();
@@ -7,13 +7,13 @@ open item and open an issue or pull request; see [CONTRIBUTING.md](../CONTRIBUTI
7
7
  Legend: ✅ available · 🟡 partial (see notes) · ❌ not yet · ➖ not planned (a deliberate choice,
8
8
  or better served by the PostgreSQL ecosystem; see the notes and [extensions](guide/15-extensions.md))
9
9
 
10
- Last reviewed: 2026-10-13 (sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
10
+ Last reviewed: 2026-10-13 (sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
11
11
 
12
12
  ## At a glance
13
13
 
14
14
  | Area | ✅ | 🟡 | ❌ | ➖ | In short |
15
15
  |---|---:|---:|---:|---:|---|
16
- | App Builder and development | 17 | 0 | 0 | 0 | Page Designer with drag-and-drop and a code editor, wizards, search, where used, an Advisor, a CLI with one file per component, page locks and comments, supporting objects, working copies with a three-way merge, theme/library/boilerplate apps with subscriptions; App Builder AI (pages, SQL and table descriptions) |
16
+ | App Builder and development | 20 | 0 | 0 | 0 | Page Designer with drag-and-drop and a code editor, wizards, search, where used, an Advisor, a CLI with one file per component, page locks and comments, supporting objects, working copies with a three-way merge, theme/library/boilerplate apps with subscriptions; App Builder AI (pages, SQL and table descriptions) |
17
17
  | Regions | 23 | 0 | 0 | 0 | All everyday regions, a search region over search configurations and a URL region; sixteen chart types with drill-down; calendars with week/day/list views and drag and drop; faceted search, smart filters and a region display selector; interactive reports with breaks, aggregates, highlights, compute, group by, pivot, chart view and saved reports; interactive grids with single row, group by and chart views; maps (with vector tiles) and trees; template components; row ranges, lazy loading and region caching for large tables |
18
18
  | Items | 13 | 0 | 0 | 0 | All common items, file upload (several files per item), rich text and Markdown editors, star rating, combobox, date range, QR code, password reveal, display image and geocoded address |
19
19
  | Logic and processing | 17 | 0 | 0 | 1 | User preferences and application settings; all dynamic action events and actions, Ajax callbacks and an error handling function; Core APEX model complete with computations, conditional branches, build options and menu buttons, download, chain and workflow processes; JavaScript from static application files in dynamic actions; region, item, dynamic action and process plug-ins |
@@ -22,12 +22,13 @@ Last reviewed: 2026-10-13 (sprint 60: isolated workspaces with their own databas
22
22
  | Globalization | 6 | 0 | 0 | 0 | One translated app like 26.1 with dynamic translations of table values; number format masks and automatic time zone; twenty-two built-in languages |
23
23
  | Data and integration | 12 | 0 | 0 | 2 | Generate DDL and schema comparison; `meta.data_export` and data export downloads (CSV, JSON, XML, HTML, Excel, PDF); e-mail through SMTP (password or OAuth2), Microsoft Graph, Amazon SES and HTTP APIs; REST APIs via PostgREST, REST data sources that write back and synchronise, web credentials with OAuth2 grants, CSV/XLSX/JSON/XML loading with saved definitions and unloading, SQL scripts and Quick SQL, report PDFs and document templates |
24
24
  | Workflow, automation and AI | 6 | 0 | 0 | 0 | Scheduled automations with several actions and runs from SQL, approvals, a task list and workflows with parallel branches, versions and invoke-API steps; AI with Claude or OpenAI: *Generate text with AI*, an assistant region with tools, natural-language report filters and blueprints |
25
- | Administration | 9 | 0 | 0 | 0 | Isolated workspaces with their own database logins and roles, and workspace administrators; application availability and Run only, automatic backups, user feedback; a developer toolbar with a session state viewer and debug per session; Top SQL per app; debug messages per request; an install/upgrade log |
26
- | **Total** | **137** | **0** | **0** | **3** | 140 APEX features compared: 98% available, none partial or missing |
25
+ | Administration | 10 | 0 | 0 | 0 | An APEX-like sign-in with a workspace; workspace message, environment banner and utilization; isolated workspaces with their own database logins and roles, and workspace administrators; application availability and Run only, automatic backups, user feedback; a developer toolbar with a session state viewer and debug per session; Top SQL per app; debug messages per request; an install/upgrade log |
26
+ | **Total** | **141** | **0** | **0** | **3** | 144 APEX features compared: 98% available, none partial or missing |
27
27
 
28
28
  (Counts are of the rows in the tables below. A review on 2026-10-06 added 18 rows for APEX features that were
29
- not compared before, and one on 2026-10-12 added two more (property help, application availability), so the share
30
- available dropped without anything being removed.)
29
+ not compared before, one on 2026-10-12 added two more (property help, application availability), and one on
30
+ 2026-10-13 compared the builder's screens with APEX 26.1 and added four (App Gallery, application groups, cross
31
+ application reports, Manage Service), so the share available dropped without anything being removed.)
31
32
 
32
33
  ## App Builder and development
33
34
 
@@ -50,6 +51,9 @@ available dropped without anything being removed.)
50
51
  | Builder search, "where used" | ✅ | Search over every page and component (names, SQL, settings, help); "Used in" under items, pages, lists of values, schemes and report layouts. No search and replace |
51
52
  | AI assistant, pages from natural language, describe tables for LLMs (26.1) | ✅ | **App Builder AI** (administrators choose the builder's AI service): SQL Workshop → AI writes SQL from a question (shown in an editor, never run automatically), explains a query or an error, and describes tables and columns for LLMs (notes, optionally `COMMENT ON`, with AI drafts from names, types and keys only); **Create pages with AI** turns a description into proposed pages that the developer edits and confirms ([chapter 3](guide/03-builder.md)). The model never sees table rows |
52
53
  | Sample data source for development (26.1) | ✅ | SQL Workshop → **Sample Data** ([chapter 16](guide/16-files.md#sql-workshop--sample-data)): generators proposed per column from the catalog and column names (names, e-mail addresses, phones, companies, addresses, words and sentences, codes, number/date/time ranges, booleans, value lists from enums and CHECKs, sequences, foreign keys picking existing or newly inserted parent rows, values relative to another column, % nulls; identity/serial/generated columns skipped; unique and simple CHECK constraints respected), rows per table, a seed for repeatable data, preview (inserted and rolled back), insert in one transaction with parents first, download as SQL INSERTs or CSV, saved generator definitions |
54
+ | App Gallery: sample, starter and custom apps | ✅ | (sprint 61) The **App Gallery** in the builder ([chapter 3](guide/03-builder.md#app-gallery)): *Sample Apps* shipped with pgkiln (HR Demo, Tasks) installed by administrators; *Starter Apps* (Team Tasks, Contacts, Expense Claims, Asset Inventory, Event Registrations) shipped as blueprints, created after their review in the current workspace with a role and schema of their own; *Custom Apps*: the workspace's boilerplates. **Missing:** a gallery fed from a remote catalogue |
55
+ | Application groups | ✅ | (sprint 61) Workspace utilities → **Application groups**: a workspace's named groups, a group per application, a group filter and labels on the App Builder home ([chapter 3](guide/03-builder.md#application-groups)) |
56
+ | Cross application reports | ✅ | (sprint 61) In Workspace utilities' side column, over every application of the workspace: application attributes, build options, developer comments, authentication, authorization schemes, password items and theme summary |
53
57
 
54
58
  ## Regions
55
59
 
@@ -60,7 +64,7 @@ available dropped without anything being removed.)
60
64
  | Interactive grid | ✅ | Inline edit, add and delete rows, lists of values, required columns, per-row errors, all-or-nothing save, signed row keys, search and paging; **aggregates** over the whole result (developer and user defined), **frozen columns**, column **reorder/resize/hide** (Actions → Columns, or drag and drop), per-user layouts and **saved grid reports** (private and public), **master-detail** (signed row selection, details refreshed in place), a **row actions menu** (edit, duplicate, delete, links) and **copy/paste** of cell ranges (tab-separated, spreadsheet compatible) ([chapter 4](guide/04-pages-and-regions.md)) |
61
65
  | Interactive grid views: single row view, chart view, group by | ✅ | (sprint 54) **Single row view**: one row of the search as a form with first/previous/next/last and *Add row*, from the Actions or row actions menu, saved by the grid's own process with the same checks; **group by** and **chart** set up in the Actions menu over every row of the search, as for the interactive report; `views` limits them; Save comes back to the same search, page and view ([chapter 4](guide/04-pages-and-regions.md#grid-interactive-grid)). No pivot view on grids (the interactive report has one), no editing in the single row view of columns the grid doesn't show |
62
66
  | Form (automatic row processing) | ✅ | Fetch, insert, update, delete, in a page or a modal dialog. Detects rows deleted meanwhile. (sprint 50) **Lost update detection** (APEX: *Prevent Lost Updates* and *Lock Row*): the page carries a checksum of the row's form columns (`meta.row_version`, independent of time zone and date format, as the app's role); *Save* and *Delete* lock the row and refuse when it changed meanwhile; off per form with `{"lost_update": false}` ([chapter 4](guide/04-pages-and-regions.md#form)) |
63
- | Charts | ✅ | Bar, column, stacked, line, area, combination (columns + lines), scatter, bubble, donut, pie, gauge, funnel, radar, **Gantt** (start/end, progress, milestones, dependencies, a today line, a time axis from hours to years), **pyramid** (area-proportional segments, or two series back to back) and **polar** (polar area); multi-series, tooltips, data table, palette checked for colour-vision deficiency; drill-down links on chart marks (checksummed, only to pages the user may open). Drawn on the server as SVG without inline styles. **Missing:** other Oracle JET types (e.g. stock, box plot, range), editing a Gantt by drag and drop |
67
+ | Charts | ✅ | Bar, column, stacked, line, area, combination (columns + lines), scatter, bubble, donut, pie, gauge, funnel, radar, **Gantt** (start/end, progress, milestones, dependencies, a today line, a time axis from hours to years), **pyramid** (area-proportional segments, or two series back to back) and **polar** (polar area); (sprint 63) **range** (floating columns from low to high), **box plot** (from one value per row or five statistics) and **stock** (candlesticks from open, high, low and close); multi-series, tooltips, data table, palette checked for colour-vision deficiency; drill-down links on chart marks (checksummed, only to pages the user may open). Drawn on the server as SVG without inline styles. **Missing:** other Oracle JET types (e.g. sunburst, treemap, timeline, picto), editing a Gantt by drag and drop |
64
68
  | Cards and metric cards (26.1 metric card template) | ✅ | Cards from SQL, with a KPI "metric" style |
65
69
  | Calendar | ✅ | Month, week, day and list views (agenda list on phones); create on click (a checksummed link with the slot); drag and drop to move events through the developer's move SQL, run as the app's role (CSRF, a *who may drag* authorization, only events the user sees). Works without JavaScript; dragging adds on top ([chapter 4](guide/04-pages-and-regions.md#calendar)) |
66
70
  | Faceted search | ✅ | Checkbox, range (predefined, or a custom from/to on numbers and dates), star rating and search facets with live counts that honour the other facets; exclude on checkbox facets (26.1). Values travel as query parameters, never as SQL text. **Missing:** facet charts |
@@ -179,7 +183,7 @@ available dropped without anything being removed.)
179
183
 
180
184
  | APEX | pgkiln | Notes |
181
185
  |---|---|---|
182
- | SQL Workshop: SQL commands, object browser | ✅ | The object browser shows columns, RLS policies, grants, data and function source |
186
+ | SQL Workshop: SQL commands, object browser | ✅ | (sprint 61) A **home page** like APEX's: tiles, recently created tables, your recent SQL commands and the workspace's recent scripts, and the session's **default schema** (first in the search path of SQL Commands and Scripts). The object browser shows columns, RLS policies, grants, data and function source. (sprint 62) **Utilities** like APEX's: the tools on one page, **Object Reports** (tables without a primary key, unindexed foreign keys, row-level security without policies or not enabled, PUBLIC grants, unused indexes, largest tables, missing comments), **About Database** and a **Database Monitor** of sessions and blocked queries for administrators. **Missing:** a recycle bin (PostgreSQL has none), user interface defaults (pgkiln's wizards read the catalog and comments) |
183
187
  | RESTful services (ORDS) | ✅ | [PostgREST](https://postgrest.org) next to pgkiln: `api` schema, the same RLS as the UI, per-app API role, tokens in the builder, a pre-request check, and **OAuth clients** (client credentials, like ORDS `oauth.create_client`) so tokens renew themselves |
184
188
  | REST handler editor, REST-enabled SQL | ✅ | **REST modules** in the builder: handlers (method, path with parameters, SQL as collection, item or statements, roles, public) served by pgkiln with bearer tokens and an OpenAPI description; plus PostgREST for schema-wide APIs. **Missing:** REST-enabled SQL (rarely desirable) |
185
189
  | SQL scripts, query builder, Quick SQL | ✅ | **SQL Scripts**: saved, upload/download, a result per statement, stop or continue on errors, optionally one transaction, run history. **Quick SQL** (a subset: tables, child tables, types, the main column directives, `/auditcols`, settings, views). A graphical **query builder**: tables as boxes on a canvas (dragged, placed where left), joins along foreign keys or drawn from column to column (inner or left), column functions with automatic `group by`, conditions, sort, open in SQL Commands; without JavaScript the same as forms ([chapter 3](guide/03-builder.md#sql-workshop)) |
@@ -198,7 +202,7 @@ available dropped without anything being removed.)
198
202
 
199
203
  | APEX | pgkiln | Notes |
200
204
  |---|---|---|
201
- | Approvals and task list | ✅ | Task definitions (approval / action, owner roles and users, business administrators, priority, due date, details page), `meta.create_task` from application SQL, a task list region (claim, approve/reject/complete with comment, release, delegate, cancel, history), completion SQL in the same transaction. **Missing:** built-in e-mail notifications (send them with `meta.send_mail` from the completion SQL or an automation), vacation rules, expiry/escalation policies |
205
+ | Approvals and task list | ✅ | Task definitions (approval / action, owner roles and users, business administrators, priority, due date, details page), `meta.create_task` from application SQL, a task list region (claim, approve/reject/complete with comment, release, delegate, cancel, history), completion SQL in the same transaction. (sprint 64) **Vacation rules** (a stand-in acts on a user's tasks while the rule is active, checked on every request) and **expiration policies** (expire, renew up to N times, escalate to a role once; a waiting workflow goes on with the outcome `expired`). **Missing:** built-in e-mail notifications (send them with `meta.send_mail` from the completion SQL or an automation) |
202
206
  | Workflow (26.1: parallel flows, multi-tenancy) | ✅ | Workflow definitions of task, SQL, switch, wait, **invoke API** (a REST data source or URL through the invoke-API code, response values and the HTTP status into variables, retryable faults, no transaction open during the call) and end steps with variables, started from application SQL, run by the server (NOTIFY + polling) as the app's role; console region (terminate, retry a faulted step); diagram in the builder. **Parallel branches** (split, then a join that waits for all or for the first branch) and **versions** (development, active, inactive; running instances keep theirs) ([chapter 6](guide/06-processing.md#workflows)). (0.31) **Tenants**: `meta.set_tenant()` (APEX: `APEX_SESSION.SET_TENANT_ID`); workflows, their tasks, background chains, the task list, the console and every action follow the session's tenant ([chapter 6](guide/06-processing.md#tenants)). No *Send E-Mail* activity: send mail with `meta.send_mail` from an SQL step |
203
207
  | Automations (scheduled) | ✅ | Shared Components → Automations: cron schedules with time zones, once or per row of a query, **several ordered actions** with their own conditions, **error handling** (stop, skip the row and continue, or disable; row errors in the run history), roles, run history and Run now, **on-demand runs from SQL** with `meta.run_automation()` (like `APEX_AUTOMATION.EXECUTE`, synchronous in the caller's transaction), safe with several servers ([chapter 6](guide/06-processing.md#automations)) |
204
208
  | AI assistant, natural-language reports (NL2IR), AI agents and tools (26.1) | ✅ | Region type **AI assistant**: a conversation per session, context queries (RAG over developer queries, run as the app's role, read-only), and **tools** the model may call: SQL with bound arguments checked against declared parameters (read-only unless marked as writing, row limit, timeout) and REST data sources, each optionally behind an authorization scheme. **Natural-language report filters**: an "Ask in your own words" box turns a question into the report's normal filters, search and sort, limited to its visible columns ([chapter 4](guide/04-pages-and-regions.md)). Agents are defined per region (not as shared components); answers are not streamed. `pgvector` covers semantic search on the data side |
@@ -209,12 +213,13 @@ available dropped without anything being removed.)
209
213
 
210
214
  | APEX | pgkiln | Notes |
211
215
  |---|---|---|
212
- | Developer accounts | ✅ | |
216
+ | Developer accounts, workspace sign-in | ✅ | (sprint 61) The builder's sign-in page like APEX's: workspace (optional; it becomes the current one, a wrong one answers like a wrong password), username, password with a Show button, *Remember workspace and username*, a link to Administration |
217
+ | Workspace message, environment banner, workspace utilization, preferences and extension links (Manage Service) | ✅ | (sprint 61) An **Administration** menu in the icon rail; administrators and workspace administrators set a **workspace message** (on the App Builder home) and an **environment banner** (a coloured label on every builder page of the workspace), and see **workspace utilization** (applications, pages, page views, users, static files, schema storage) ([chapter 3](guide/03-builder.md#workspace-message-and-environment-banner)). (sprint 62) **Workspace preferences**: the SQL Workshop and the applications' RESTful services (REST modules) on or off per workspace; **extension links** in an Extensions menu of the rail ([chapter 3](guide/03-builder.md#workspace-preferences)). **Missing:** the account login controls of APEX's preferences (pgkiln's password policy and sign-in throttling are installation-wide) |
213
218
  | Instance administration, install/upgrade logs (26.1) | ✅ | Versioned migrations (`npm run db:migrate`), every run logged; Workspace utilities → **Installation** (administrators): server version, install and upgrade runs with errors, applied migrations, and warnings for migrations the database misses or the server doesn't know ([chapter 3](guide/03-builder.md)). Workspace utilities → **Instance settings**: session length and sign-in throttling (stored in the database, over the environment variables) and an overview of the server's configuration without secrets; a server whose database lacks migrations says so (503) or applies them (`MIGRATE_ON_START`). Other settings stay environment variables |
214
219
  | Debug messages | ✅ | Debug levels 1–9 per app; `meta.debug(level, text)` and `meta.debug_enabled(level)` from application SQL (APEX_DEBUG); timed entries per request (page steps, regions, processes, branches, errors, SQL notices); Activity → **Debug messages** viewer per page view with filters and the slowest step highlighted; retention 1–90 days; password item values are never recorded ([chapter 6](guide/06-processing.md)). (sprint 50) Debug for **one session** from the developer toolbar, the application staying out of debug. **Missing:** debug entries for background jobs and REST requests |
215
220
  | Developer toolbar, session state viewer | ✅ | (sprint 50) Developers signed in to the builder see a toolbar on the pages of their applications in the same browser: App Builder, the application, **Edit page** (Page Designer), **Session** (the session state viewer: user, roles, session, language, time zone, debug level, this page's items, application items and other pages' items; passwords never shown; clear the page's items) **Quick edit** (sprint 59: click a region, item or button to open it in the Page Designer) and **Debug** for this session only. A separate random token (cookie path `/a`, HttpOnly) of a live builder session of a developer of the application's workspace ([chapter 3](guide/03-builder.md#developer-toolbar)). **Missing:** Theme Roller from the page |
216
221
  | Feedback (users send feedback from a page, developers answer) | ✅ | (sprint 51) With *Users can send feedback* on, the navigation bar's **Feedback** link opens a page for a comment, idea or problem about the page (optional rating), with the user's earlier feedback and the developers' answers; developers read, filter, set a status and answer under Application → Feedback ([chapter 3](guide/03-builder.md#feedback)). No screenshots, no e-mail to developers |
217
- | Automatic application backups, page groups | ✅ | (sprint 51) **Backups**: before every import that replaces an application (CLI, MCP, working copy merge, restore), daily when it changed, and by hand; the newest 25 (configurable) are kept; download and restore from the builder ([chapter 3](guide/03-builder.md#backups)). (sprint 58) **Page groups**: Shared Components → Page groups, a page's *Page group*, the page list by group with a filter ([chapter 3](guide/03-builder.md#page-groups)) |
222
+ | Automatic application backups, page groups | ✅ | (sprint 51) **Backups**: before every import that replaces an application (CLI, MCP, working copy merge, restore), daily when it changed, and by hand; the newest 25 (configurable) are kept; download and restore from the builder ([chapter 3](guide/03-builder.md#backups)); (sprint 62) Workspace utilities → **Manage backups** across the workspace's applications, with a backup of all of them at once. (sprint 58) **Page groups**: Shared Components → Page groups, a page's *Page group*, the page list by group with a filter ([chapter 3](guide/03-builder.md#page-groups)) |
218
223
  | Application availability (Available, Developers only, Restricted access, Unavailable; build status *Run only*) | ✅ | (sprint 51) Available, available to developers only (a builder session in the same browser), restricted to named users, or unavailable with a message, for pages, file downloads, REST modules and PostgREST; build status **Run only** refuses builder changes as if the application were locked, while `pgkiln import --replace` still deploys. Installation settings: not exported, kept by an import ([chapter 3](guide/03-builder.md#availability-and-build-status)) |
219
224
  | Monitoring (top SQL) | ✅ | Activity per app; Top SQL per app's database role from `pg_stat_statements` (sortable, resettable) |
220
225
  | Workspaces (multi-tenant) | ✅ | Workspaces group applications and developers: the builder shows and opens only the applications of a developer's workspaces (administrators: all), a current workspace with a switcher, new and imported applications go into it, administrators add workspaces, choose their developers and move applications ([chapter 3](guide/03-builder.md#workspaces)). (sprint 60) **Isolated workspaces** are a tenant boundary: the applications run on a PostgreSQL login of the workspace's own and its developers' SQL Workshop on another, with row-level security on pgkiln's tables, so SQL that resets its role can't reach other workspaces' applications, data, sessions or accounts; **workspace database roles** (APEX: workspace schemas) assigned by administrators; **workspace administrators** who manage their workspace's developers; SQL scripts, blueprints and sample data per workspace ([isolated workspaces](guide/03-builder.md#isolated-workspaces)). **Missing:** user directories per workspace (accounts are installation-wide, visible to the workspaces whose applications they use), developers created by a workspace administrator |