- Move the login page spacing and radii onto the 4/8/12/16/24/32 token
scale (shell paddings, form gaps, input radii, mobile breakpoints)
- Align shared form containers: edit-card margins, card-footer gap,
matrix toolbar footer, breadcrumb strip, dropdown identity row
- Normalize card internals to tokens: StatCard row gap, DashboardCard
title bar, AlertList group padding, home widgets, NavMenu items,
things-dialog body, MCP/role/user assign views, point value card
- Use the 8px page gutter (--dc3-space-2) for home row rhythm, the stat
grid gap, and the SLA strip margin so vertical spacing matches the
horizontal card gutters
- Normalize the home banner internals to the token scale: 16px body
padding and area gap, 8x16 clock box, 8x12 service chips
- Normalize the SLA strip padding/radius and chip spacing to tokens
- Align things-card vertical margin with its documented 8px rhythm so
settings grids match the horizontal gutter
- Unify the two 12px form gutters and the breadcrumb strip padding on
the same scale
- Extract a shared BrandLockup (logo, gradient wordmark, AGENTIC tag,
signal line) used by both the header brand capsule and the login page
- Add a shared glass-capsule SCSS recipe so the header capsules and the
login brand pill keep identical geometry and glass decoration
- Align the login page with the workspace font stack
- Reduce the account trigger to an avatar and move the username into a
themed identity dropdown with divider; style the popper with dc3
tokens so light and dark themes both follow the system theme
- Point the About action at the live site domain (https://dc3.site)
- migrate auth, manager, data, and agentic persistence to R2DBC
- hard cut pagination, facade, gateway, web, and CLI contracts
- remove legacy database and TSDB adapters and document validation gates
BREAKING CHANGE: platform persistence now requires PostgreSQL 18 with TimescaleDB and the new reactive API contracts.
dc3 session (list/get/messages/rename/delete) over the conversation
routes and dc3 action (pending/confirm/reject) for the high-risk
tool-call approval loop, closing the last P0 coverage gap from the
2026-08-27 audit. Six suites assert path shaping, query encoding and
method selection.
- dc3 analytics run <op> --args posts to the nine /api/v3/data/analytics
operations (S19 agent face); analytics list prints the catalog
- dc3 alert gains activity/storm-sources/flapping/correlation/
peer-deviation/aging/mtta/change-impact (GET), latency, silent-sources,
coverage-gap and bulk-confirm, with snake_case param mapping and
generic k=v passthrough
- seven new suites cover path shaping, param mapping, passthrough,
bulk body verbatim and op validation
- gateway container env passes DC3_GATEWAY_OAUTH_ENABLED/JWKS_URL so
deployments can turn RS256 verification on without image changes
- PrincipalHeader regains a 7-arg constructor delegating scopes=null,
keeping ~15 existing call sites compiling against the new field
- dc3-common-auth imports com.baomidou.mybatisplus.spring.service
directly since the 3.5.17 split but never declared the artifact;
out-of-reactor consumers hit FileNotFoundException at boot. Manage
it in the root BOM alongside the existing entry.
- ci-cli.yml gates dc3-cli (lint, test, build, help smoke) on PRs
touching dc3-cli/**
- adapt AuthenticGatewayFilterTest to the extended constructor
- generate responses now acknowledge with data:"ok" and deliver the
ticket via the dc3-token cookie; extractTicket prefers a JWT-shaped
body (June-era builds) and falls back to the cookie
- setProfile validated an empty template instead of the merged value,
crashing every config write on profiles lacking username; validate
the merge instead. Live-verified end to end against auth+gateway
First Phase-3 slice: dc3 tools list/call speak JSON-RPC to POST /mcp
with the same Bearer ticket as REST, reading the live tool catalog
instead of hard-coded paths. Requires an oauth-type login; classic
tickets get a clean exit-code-3 auth error. Five unit suites cover
request shaping, scope rejection, rpc errors and the 401 challenge.
Phase 2 of token-unification-mcp-first-cli.md:
- capScopesByRbac narrows requested scopes at authorization time to
what the principal's bound resources cover via the tool catalog
(additive per risk class; never widens). principals with no
bindings degrade to legacy behaviour so incomplete RBAC setups
cannot silently zero out clients; toggle dc3.oauth.rbac-scoped-scopes
- risk-ladder lifetime: call-capable tickets keep the 15m default,
list/read-only tickets get a new readOnlyAccessTokenTtl of 1h
dc3 auth login --oauth exchanges a registered MCP client id/secret
for an RS256 access ticket at /oauth2/token and stores it as an
oauth-type state whose requests travel as Authorization: Bearer.
Renewal is intentionally not attempted without a stored secret;
expiry surfaces a clean auth error. Option is hidden from help until
gateway-side RS256 verification is enabled in deployments.
Phase 1 of token-unification-mcp-first-cli.md:
- shared OAuthJwtVerifier in dc3-common-public; auth center's
parseAccessToken now delegates to it
- gateway TokenResolver chain: optional OAuthTokenResolver verifies
bearer tickets against a cached JWKS (unknown kid forces one
refresh) and projects claims onto the extended PrincipalHeader
(new scopes field); behind dc3.gateway.oauth.enabled, default off
- platform login-token lifetime configurable via DC3_TOKEN_TTL_HOURS /
dc3.token.ttl-hours; denylist cache follows ttl+1h so revocation
keeps outliving tokens
- projection is additive per risk class: a mixed LOW/HIGH principal
keeps tools_call; MEDIUM (hidden-until-enabled) now accounted
- qualify high-risk 'no standing grant' to the default path so it no
longer contradicts the approved --approve-window escape hatch
- note phase-1 ordering: --oauth login can land before the gateway
resolver chain, behind a flag until REST accepts RS256
Revert the premature McpServer.vue change (implementation was not
requested). Record the onboarding UX contract in
token-unification-mcp-first-cli.md §4.1 instead, and rename the CLI
package back to dc3-cli per review.
Ready-to-copy shell block (install, set gateway to the current
origin, interactive login) next to the existing agent config
snippets, plus en/zh i18n keys for the new card.
Add design draft covering:
- verified state of the two parallel auth systems (HS256 login vs
RS256 OAuth/MCP) and their incompatible verdict points
- gateway TokenResolver chain over an extended X_AUTH_PRINCIPAL so
both tickets converge on one RBAC-backed permission model
- scope-as-projection of dc3_resource bindings, risk-layered TTLs
with step-up for high-risk calls
- CLI rebuild around the MCP/OAuth credential mechanism (supersedes
dc3-client-sdk.md Phase 4) with five open decision questions
Move the previously untracked TypeScript CLI into iot-dc3 as an
independently managed module next to dc3-web:
- pnpm workspace (pnpm@11.24.0) with esbuild build approval,
package renamed to iot-dc3-cli, npm lockfile dropped
- add eslint flat config; lint now runs green after removing
unused imports/vars and adding switch-case breaks in config.ts
- document CLI conventions and commands in AGENTS.md
- driver replicas are per-instance nodes with their own RabbitMQ
command queues (group=dc3.driver.client, key service.node), not
workers over shared queues; FAQ now also states Compose/Swarm keep
protocol drivers at 1 replica (shared driver_data / SQLite outbox)
- fail-fast gate covers AUTH_HMAC_SECRET only; DC3_SECURITY_KEY is
non-empty-checked, so a weak default still boots
- 6271 is intended as the device UDP channel but every publish site
omits /udp / protocol: UDP and actually publishes TCP - say so
- k8s scaling note: scaled driver pods are independent nodes, not one
shared worker pool
- swarm runbook: pushing dependency images is a multi-node requirement,
a local build is fine on a single node
- USAGE.md: NODE_ENV bullet narrowed to weak AUTH_HMAC_SECRET rejected
Translate the seven remaining English documents under docs/ in place,
preserving all technical content, code blocks, identifiers, links, and
decision IDs (D1-D19, F1-F15, P0-P5). Add a categorized Chinese
navigation index (docs/README.md) with per-document summaries, the
storage supersession chain, and task-based reading paths.
Approved design for replacing MyBatis-Plus + JDBC with Spring Data R2DBC,
auth center as the pilot. Records the verified fact base (F1-F15), the
D1-D19 decision checklist (per-dialect repository modules, TCK discipline,
compile-time tenant scoping, facade/gRPC blocking edge, timestamptz codec,
workerId allocation rule), and the P0-P5 phased plan with the P2-P3
go/no-go gate. Supersedes storage-abstraction.md section 3 (relational
half); its TSDB half was already superseded.
Replace limit-truncated listing with real server-side paging
(LIMIT/OFFSET plus a shared count query) behind Page envelopes:
tool/list accepts a nested Pages block and audit/list takes
current/size params, both keeping the legacy limit as a compatibility
page size. The frontend configs forward pager state and consume
{records, total} instead of faking a single page; the assignment
drawer keeps its full-catalog semantics with one 500-row page (the
service cap). Adds a mapper XML grammar gate and audit page/count
symmetry tests.
Co-Authored-By: Claude <noreply@anthropic.com>
The "(? IS NULL OR ? = '' OR ...)" filter shape breaks on Postgres with
stringtype=unspecified: a bare bind parameter in an IS NULL context has
no inferable type, so opening the MCP tool page with a blank risk-level
filter failed with "could not determine data type of parameter $1". The
dynamic <if> form drops the dead branches entirely and is
dialect-neutral, so the mysql/mariadb forks of the statement are gone.
Co-Authored-By: Claude <noreply@anthropic.com>
The history JDBC url sets only currentSchema=dc3_history, so any
object the history connection must resolve outside its own schema
(extension functions, shared types, public tables) fails with the
default search_path stripped to a single entry. Append public to the
currentSchema list across the dev/pre/pro/test profiles so unqualified
names in the history route resolve against dc3_history first and fall
back to public, matching standard PostgreSQL search-path semantics.
Co-Authored-By: Claude <noreply@anthropic.com>
Extract the header language switch into a reusable AppPreferences
component and add a light/system/dark theme toggle beside it, backed by
the persisted app store theme mode. Use the component in both the
console header (compact) and the login page (surface variant) so the
public page obeys the same theme contract as the workspace.
Tokenize the login page palette in theme.scss with dark-mode twins,
switch the login validation rules to computed form rules so messages
follow locale changes, and extend the layout component test and the
login/responsive e2e specs for the new controls.
Co-Authored-By: Claude <noreply@anthropic.com>
Bump pnpm to 11.24.0 and refresh element-plus, axios, dayjs, dompurify,
marked, pinia, vue-i18n, playwright, typescript-eslint, eslint, sass,
vite and vitest within their minor lines, and widen the pnpm-workspace
onlyBuiltDependencies ranges accordingly.
Keep typescript on ~6.0.3, vue on ^3.5.40 and happy-dom on ^20.11.0:
typescript 7 drops the ./lib/tsc subpath vue-tsc still resolves, vue
3.5.41 makes compiler-sfc require filesystem access for imported prop
types under the vitest environment, and happy-dom 20.11.6 changes the
execCommand clipboard behaviour the utils suites assert on.
Co-Authored-By: Claude <noreply@anthropic.com>
affectData is not declared for insert by the MyBatis mapper DTD, so
the attribute only surfaces as a startup error after the affected
module deploys. Remove it from every upsertLeaseState variant and
document that the atomically advanced assignment version is re-selected
in the same transaction rather than returned.
Add ManagerMapperXmlGrammarTest to parse every mapping/*.xml against
the bundled DTD so future invalid attributes fail the build instead of
application startup.
Co-Authored-By: Claude <noreply@anthropic.com>
The auth filter sits in front of the routing chain, so TCP connect
refusals and timeouts propagate through it and masquerade as an
authentication failure answered with 500. Classify ConnectException
and ConnectTimeoutException (including wrapped causes) as downstream
unavailability and answer 503 Service Unavailable instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Spring Boot 4 moved EnvironmentPostProcessor from
org.springframework.boot.env to org.springframework.boot, so the
spring.factories registration under the old key no longer loads
ActiveRabbitProfileConfig and RabbitEnvironmentConfig and the
rabbit profile defaults silently disappear.
Co-Authored-By: Claude <noreply@anthropic.com>
The tsdbDataSource bean breaks by-type injection: the dynamic routing
dataSource carries no @Primary, so a candidate-eligible second
DataSource throws NoUniqueBeanDefinitionException in consumers that
cannot take a qualifier (MyBatis-Plus sqlSessionFactory, Quartz, the
health check). Mark it defaultCandidate = false so those consumers see
exactly one candidate again while the TSDB adapter keeps resolving it
through @Qualifier("tsdbDataSource").
The same second bean makes @ConditionalOnSingleCandidate(DataSource)
fail, so DataSourceTransactionManagerAutoConfiguration silently backs
off and no TransactionTemplate is auto-configured. Declare the
DataSourceTransactionManager explicitly, bound to the routing
datasource with local-transaction semantics: switching routes inside
an open transaction does not take effect.
Co-Authored-By: Claude <noreply@anthropic.com>
Refresh the wrapper scripts to wrapperVersion 3.3.4 (only-script
distribution) and move distributionUrl from 3.9.11 to 3.9.16, the
newest 3.9.x release. Pin line endings for mvnw, mvnw.cmd and the
wrapper properties in .gitattributes so the scripts stay executable
across platforms.
Co-Authored-By: Claude <noreply@anthropic.com>
- Spring Boot 4.0.7 -> 4.1.1, Spring Cloud 2025.1.2 -> 2025.1.3,
Spring AI 2.0.0 -> 2.0.1, springdoc 3.0.3 -> 3.1.0
- Migrate gRPC starters to the Boot-owned spring-boot-starter-grpc-client/server
line (org.springframework.grpc starter stops at 1.0.3 and mixes badly with the
spring-grpc-core 1.1.1 managed by the Boot 4.1 BOM), and move the
spring.grpc.channels.* YAML keys to the new spring.grpc.channel.* format
- Refresh driver protocol libraries (modbus4j 3.1.0, jSerialComm 2.11.4,
bacnet4j 6.1.1, gurux-dlms 4.0.96, j60870 1.8.0, snmp4j 3.13.1) and drop the
flow-control overrides removed from the modbus4j SerialPortWrapper interface
- Refresh jackson 2.x/3.x, protobuf, okhttp, micrometer, oshi, sqlite-jdbc,
swagger-core, instancio, mockito, logstash-logback-encoder 9.0 and the
graalvm native plugin; keep commons-io (broken timestamp-version candidate)
and j-interop 2.0.4 (legacy major) pinned
Co-Authored-By: Claude <noreply@anthropic.com>