refactor: remove redundant EmptyObject from CommandResponse union
EmptyObject (Record<string, never>) is structurally assignable to every
object type in the union. Empty responses are handled at runtime via
isEmpty() checks before reaching type-narrowing logic.
ci: update Python matrix from 3.12/3.13 to 3.13/3.14
Python 3.12 is in security-only mode (no more bugfixes).
Python 3.14 is stable with active bugfix support (3.14.3 released).
Run typecheck, lint, and coverage on 3.14 (latest stable).
feat(ui-cli): add custom JSON payload option for OCPP and transaction commands
Add -p, --payload <json|@file|-> to all 16 OCPP commands and both
transaction commands. Supports three input modes:
- Inline JSON: -p '{"reason":"PowerUp"}'
- File: -p @collections/boot.json
- Stdin: echo '{}' | evse-cli ocpp heartbeat -p -
Payload is merged beneath command-specific options (--id-tag, etc.)
so CLI flags always take precedence.
Implementation:
- New resolve-payload.ts: async resolver with validation (object check,
empty check, JSON parse error with preview)
- Payload resolution integrated into runAction try/catch for clean
error handling (no stack traces on user input errors)
- PAYLOAD_OPTION/PAYLOAD_DESC constants shared via payload.ts
- 11 unit tests for resolvePayload (inline, @file, edge cases)
- README and SKILL.md updated with examples
feat(ui-cli): add registration and connector columns to station list, fix command semantics
Station list improvements:
- Add Registration column (Accepted/Pending/Rejected) after Hash ID
- Add Connectors column with per-connector status letters (1:A 2:O etc.)
- Two-letter abbreviations for ambiguous statuses (Fi/F, SE/SS)
- Remove dead countConnectors function, replace with formatConnectors
- Replace countConnectors tests with formatConnectors tests (7 tests)
Command description semantics:
- All OCPP: 'Send OCPP X' -> 'Request station(s) to send OCPP X'
- Connection: 'Open/Close WebSocket connection' -> '... to CSMS on station(s)'
- Connector: 'Lock/Unlock a connector' -> '... connector on station(s)'
- Transaction: 'Start/Stop a transaction' -> '... on station(s)'
- SKILL.md: 'send OCPP messages' -> 'trigger OCPP messages from stations to the CSMS'
- README: updated accordingly
refactor(ui-cli): move StationListPayload to shared types, add resolution error context
- Move StationListPayload from renderers.ts to types.ts to fix
cross-layer dependency (action.ts no longer imports from output layer)
- Wrap hash prefix resolution errors with contextual message
('Failed to resolve hash ID prefixes: ...')
feat(ui-cli): short hash prefix matching, human output formatters, embedded agent skill
Short hash resolution:
- Resolve hash ID prefixes via station list lookup before command execution
- Skip resolution when all IDs are full-length (>= 48 chars, SHA-384)
- Clear error messages for ambiguous or unknown prefixes
- Silent mode suppresses spinner during internal resolution call
Human output formatters:
- Borderless table renderers for station list, template list, simulator state, performance stats
- Shared format utils: truncateId, statusIcon, wsIcon, fuzzyTime, countConnectors
- Truncate hash IDs in all human output tables (station list + success/failure)
- Type guards with Array.isArray + null checks for safe payload dispatch
- Handle supervisionUrls as string | string[], optional configuration
- Dynamic status capitalization, shared captureStream test helper
- 70 new tests (format + renderers), using canonical ChargingStationData type
Embedded agent skill:
- `skill show` prints embedded SKILL.md to stdout
- `skill install [--global] [--force]` writes to .agents/skills/
- SKILL.md embedded at build time via esbuild define
- Follows AgentSkills.io open standard
CLI option rename:
- Global --url renamed to --server-url (explicit intent)
- supervision set-url --url renamed to --supervision-url (no collision)
README restructured with prefix matching docs, output modes table,
station delete options, grouped simulator vs local commands
CLI output renderers:
- Add borderless table renderers for station list, template list,
simulator state, and performance statistics
- Shared format utils: truncateId, statusIcon, wsIcon, fuzzyTime,
countConnectors using canonical ConnectorEntry/EvseEntry types
- Type guards with Array.isArray + null checks for safe dispatch
- Handle supervisionUrls as string | string[] (defensive normalize)
- Optional configuration in SimulatorState guard (graceful degradation)
- Dynamic status capitalization in generic payload display
- Extract captureStream test helper to tests/helpers.ts
- 70 new tests (40 format + 30 renderers), 124 total CLI tests
Shared type fixes (ui-common):
- Add provisioned field to TemplateStatistics
- Add timestamp field to ChargingStationData
- Add configuration (optional) to SimulatorState
CLI option rename:
- Rename global --url to --server-url (explicit intent)
- Rename supervision set-url --url to --supervision-url (no collision)
- Update types, consumers, integration tests, README
fix(ui-cli): resolve --url option collision between global and supervision
Rename global --url to --server-url and supervision set-url --url to
--supervision-url to prevent Commander option shadowing. Update types,
action consumer, integration tests, and README documentation.
fix(tests): use async mock callbacks for all async method mocks
Sync functions returning Promises are not behaviorally equivalent to
async functions. Align all 40 mock callbacks across 14 test files to use
async when the real method is async.
fix(tests): sync mock signatures with real async implementations
Two mocks returned Promises from sync functions instead of using async,
mismatching the real method signatures of sendTransactionEvent and
requestHandler.
fix(ocpp2): restore connector cleanup in TransactionEvent(Ended) response handler
Commit 710db15c removed cleanup from the Ended response handler assuming
callers always handle it. The UI broadcast channel passthrough bypasses
all caller-side cleanup, leaving connectors stuck in Occupied state.
Restore cleanupEndedTransaction in handleResponseTransactionEvent for the
Ended case, making the response handler the authoritative cleanup point
for all online paths. Add idempotency guard to cleanupEndedTransaction
so existing caller-side calls (needed for offline fallback) become no-ops
when the response handler already ran. Fix unconditional Available status
to respect ChangeAvailability(Inoperative) per G03.
fix(ui-web): force toggle button re-mount on navigation back to main view
resetToggleButtonState() only cleared localStorage but never triggered
ToggleButton re-mount, leaving its reactive state stuck on 'active'.
Watch route changes to CHARGING_STATIONS and call refresh() to re-mount
both the header toggle and CSTable children, ensuring all shared toggle
buttons re-read their (now cleared) localStorage entries.
fix(ui-web): fix remaining for...in on localStorage and remove dead test config
- Replace for...in with Object.keys() in deleteLocalStorageByKeyPattern
- Remove dead $route/$router globalProperties from ChargingStationsView
test (component now uses useRoute/useRouter composables via vi.mock)
- Fix ToggleButton.vue: replace unsafe for...in on localStorage with
Object.keys(), cache localStorage value (4 reads reduced to 1)
- Extract 42-line inline @change handler in ChargingStationsView.vue
to handleUIServerChange method
- Move ServerFailureError from WebSocketClient.ts to errors.ts
(proper separation of concerns, re-exported for backward compat)
- Simplify useExecuteAction callback param: remove union type,
use only ExecuteActionCallbacks object; update call sites
- Remove unnecessary String() in cli table.ts template literals
refactor(ui-web): remove duplicated tests for functions now in ui-common
convertToBoolean, convertToInt, and UUID tests were duplicated between
ui-web and ui-common. Tests belong in the package that owns the code.
Canonical tests are in ui-common; removed duplicates from ui-web.
refactor(ui): move generic utilities to ui-common and add useFetchData composable
- Move convertToBoolean(), convertToInt() to ui-common/src/utils/converters.ts
- Move getWebSocketStateName() to ui-common/src/utils/websocket.ts using
portable WebSocketReadyState enum instead of browser WebSocket constants
- Add useFetchData() composable to deduplicate fetch-with-loading-guard
pattern in ChargingStationsView (3 instances collapsed)
- All consumers import directly from ui-common, no re-exports
fix(ui-web): remove orphaned CSS classes and guard onFinally in useExecuteAction
- Remove dead connectors-table__row/column classes from CSConnector.vue
- Wrap onFinally callback in try/catch inside .finally() to prevent
swallowing the original rejection error
docs: add ADR for config loading strategy and ClientConfig derivation
Update WebSocketClient usage example to use Protocol.UI and
ProtocolVersion['0.0.1'] enum values (matching the now-enum-typed
ClientConfig). Add Architecture Decisions section documenting:
- ADR 1: why CLI and Web have separate config loaders (different I/O)
- ADR 2: why ClientConfig = Omit<UIServerConfigurationSection, 'name'>
chore: hoist shared devDependencies to root workspace
Remove @types/node, cross-env, prettier, rimraf, tsx, typescript from
ui/common, ui/web, and ui/cli package.json — they are already declared
in the root package.json. cli-specific deps (@types/ws, esbuild,
esbuild-plugin-clean) are kept in ui/cli.
Create ui/common/tests/mocks.ts with createMockWebSocketLike() — a factory
returning a MockWebSocketLike with trigger methods (triggerOpen, triggerClose,
triggerError, triggerMessage) and sentMessages capture array. Replace the
65-line inline createMockWs() in WebSocketClient.test.ts with an import
from the shared factory.
refactor(ui-common): generic WebSocket adapter factory with converter injection
Create ui/common/src/client/adapter.ts — a generic createWsAdapter(ws, options)
factory that implements all WebSocketLike callback boilerplate once. Both adapters
become thin wrappers specifying only their platform-specific data converter:
- createBrowserWsAdapter: data => data as string, errorDefault: 'WebSocket error'
- createWsAdapter (CLI): toDataString() for Buffer/ArrayBuffer, errorDefault: 'Unknown error'
Net reduction: ~143 lines of duplicated boilerplate eliminated.
Add DataConverter type and tests for the generic factory.
Create ui/cli/src/utils/errors.ts with extractErrorMessage(error: unknown): string
and replace 3 inline occurrences of the pattern across formatter.ts, json.ts,
and loader.ts.
refactor(ui-common): derive ClientConfig and AuthenticationConfig from Zod schemas
Replace two hand-written interfaces in client/types.ts with type aliases
derived from the Zod-inferred UIServerConfigurationSection:
- AuthenticationConfig = NonNullable<UIServerConfigurationSection['authentication']>
- ClientConfig = Omit<UIServerConfigurationSection, 'name'>
This eliminates duplication: protocol/version now carry enum types (Protocol,
ProtocolVersion) instead of loose strings. Update WebSocketClient.test.ts to
use Protocol.UI and ProtocolVersion['0.0.1'] enum values accordingly.
- Remove dead duplicate UI_WEBSOCKET_REQUEST_TIMEOUT_MS from ui/web/src/composables/Constants.ts
- Add DEFAULT_HOST ('localhost') and DEFAULT_PORT (8080) to ui/common/src/constants.ts
- Update ui/cli/src/config/defaults.ts and loader.ts to import from ui-common
- Update ui/web/tests/unit/constants.ts to use DEFAULT_HOST/DEFAULT_PORT from ui-common
- Update ui/cli/tests/config.test.ts to import from ui-common
refactor(ui-common): remove UIServerConfig alias, single canonical name
UIServerConfigurationSection is the only exported name for the UI server
config type. The UIServerConfig alias added for backward compat is removed;
CLI consumers (loader.ts, lifecycle.ts) migrated to the canonical name.
README updated accordingly.
test: add enum rejection tests and fix dynamic import in CLI test
- Add 2 edge-case tests for invalid protocol/version enum values
(protocol: 'ws' and version: '2.0' are now correctly rejected)
- Replace unnecessary dynamic import with static import in
lifecycle.test.ts for consistency with other test files
refactor(ui-common): derive UIServerConfigurationSection from Zod schema
Make uiServerConfigSchema the single source of truth for the UI server
configuration shape. The hand-written interface in ConfigurationType.ts
is removed; UIServerConfigurationSection is now inferred via z.infer<>.
- Tighten schema: protocol and version use z.enum() instead of z.string()
(follows existing z.enum(AuthenticationType) pattern, Zod v4)
- Export UIServerConfigurationSection as the primary inferred type
- Export UIServerConfig as a backward-compat alias
- ConfigurationType.ts imports UIServerConfigurationSection from schema.ts
and re-exports only ConfigurationData
- CLI defaults.ts uses Protocol.UI and ProtocolVersion['0.0.1'] enum values
instead of plain string literals to satisfy the tightened type
- CLI lifecycle.test.ts updated to use enum values in typed config literal
Zero duplicate field definitions. All quality gates pass.
feat(ui): add CLI client and shared UI common library (#1789)
* feat(ui): scaffold ui/common and ui/cli workspaces
- Add ui/common workspace with shared protocol types, SRPC WebSocket client,
UUID utilities, config types, and Zod validation schemas (21 tests, 0 errors)
- Add ui/cli workspace scaffolding with all configuration files
- Register both workspaces in pnpm-workspace.yaml
- Update .prettierignore, release-please config and manifest
- Add build-common (library, single env) and build-cli (3x3 matrix) CI jobs
- Add formatting steps for new workspaces in autofix.yml
- Add sonar-project.properties for both workspaces
* feat(cli): implement CLI core infrastructure
- Commander.js entry point with 10 subcommand groups (simulator, station,
template, connection, connector, atg, transaction, ocpp, performance,
supervision)
- Config loading with lilconfig + Zod validation + merge precedence
(defaults < config file < CLI flags)
- Output formatters: JSON (--json flag) and table (human-readable)
- WS client lifecycle: executeCommand() connects, sends, receives,
disconnects; registerSignalHandlers() for SIGINT/SIGTERM
- Typed error classes: ConnectionError, AuthenticationError,
TimeoutError, ServerError
- esbuild bundle with version injection and shebang
- 23 unit tests passing, zero lint warnings
* feat(cli): implement all 35 UI protocol command groups
Shared runAction() helper DRYs up all command action handlers.
* test(cli): add integration tests
- 8 integration tests covering --help, --version, subcommand help,
connection error handling, JSON mode, and missing required options
- Separate test:integration script targeting tests/integration/
- Unit tests narrowed to tests/*.test.ts (no integration overlap)
* docs(cli): add README for ui/cli and ui/common
- ui/cli/README.md: installation, configuration, all command groups
with examples, global options, exit codes, environment variables,
available scripts
- ui/common/README.md: exported API reference (types, WebSocketClient,
config validation, UUID utilities)
* [autofix.ci] apply automated fixes
* fix(cli): improve error handling for config and connection failures
- Catch config file ENOENT in loader.ts and throw clean error message
- Move loadConfig inside try/catch in runAction to prevent stack traces
- Use event.error in WebSocketClient onerror for better error propagation
- Separate connect errors from request errors in lifecycle.ts
- Include cause message in ConnectionError for descriptive output
* fix(cli): address PR review feedback
- Fix onerror stale after connect: replace with persistent error handler
that fails all pending sendRequest promises immediately on WS error
- Fix dead code: call registerSignalHandlers() in cli.ts for SIGINT/SIGTERM
- Fix JSON error output: write to stdout (not stderr) in --json mode to
match documented contract and enable scripting with 2>/dev/null
- Fix process.exit() in action.ts: use process.exitCode for proper async
cleanup and testability
- Fix Map iteration: use snapshot+clear pattern in clearHandlers/failAllPending
- Fix empty array edge case in table formatter: check .length === 0
- Fix README: merge exit codes 1+2 into single row (Commander uses 1)
- Fix CI: add needs: build-common to build-cli job
* fix(cli): address second round of PR review feedback
- Fix ServerFailureError: WebSocketClient.handleMessage now rejects
with a typed Error (carrying the ResponsePayload) instead of a raw
object, preventing [object Object] in CLI output
- Fix table formatter: FAILURE responses now display hashIdsFailed/
hashIdsSucceeded tables instead of early-returning with minimal info
- Fix auth schema: Zod refinement requires username+password when
authentication enabled with protocol-basic-auth
- Fix AuthenticationConfig.type: use AuthenticationType enum instead
of plain string for compile-time safety
- Fix signal handlers: use process.exitCode instead of process.exit()
so finally blocks in executeCommand can run cleanup
* fix(cli): restore process.exit in signal handlers, fix type mismatch, remove dead code
- Signal handlers: restore process.exit() — setting only process.exitCode
keeps the process alive since registering SIGINT listener removes Node's
default termination behavior
- BroadcastChannelResponsePayload: align field name with server wire format
(hashId: string | undefined, not id: string)
- Remove unused DEFAULT_TIMEOUT_MS export from defaults.ts
* refactor(ui-common): extract WS timeout to shared constant
Move UI_WEBSOCKET_REQUEST_TIMEOUT_MS from a private constant in
WebSocketClient.ts to ui/common/src/constants.ts as a single source
of truth, exported via barrel for consumers.
* refactor(cli): merge duplicate ui-common and commander imports
Consolidate split type/value imports from the same module into single
import statements using inline type syntax (`import { type X, Y }`).
Resolves SonarCloud 'imported multiple times' warning.
* refactor(cli): use Number.parseInt instead of parseInt
Resolves SonarCloud 'Prefer Number.parseInt over parseInt' warning.
* refactor(cli): remove unnecessary Command alias from commander import
Use import { Command } from 'commander' directly instead of aliasing
to Cmd. Single import serves both type and value usage.
- parseCommaSeparatedInts now rejects NaN values with a clear error
message instead of silently sending garbage to the server
- lilconfig search() path now wrapped in try/catch like the explicit
config path, giving consistent error messages for malformed configs
- Bundle all dependencies into single 504KB dist/cli.js (no node_modules
needed at runtime). Only ws native addons (bufferutil, utf-8-validate)
are external — ws falls back to pure JS automatically.
- Replace lilconfig with direct XDG config file reading:
${XDG_CONFIG_HOME:-~/.config}/evse-cli/config.json
- Remove lilconfig dependency
- Add install.sh: builds CLI, copies to ~/.local/bin/evse-cli, creates
default XDG config, warns if ~/.local/bin not in PATH
- Isolate ALL config tests from host env via XDG_CONFIG_HOME in
beforeEach/afterEach + add XDG auto-discovery happy path test
- Guard against JSON array in config file
- Update README: standalone install instructions + XDG config location
* [autofix.ci] apply automated fixes
* fix(cli): address review feedback round 3
WebSocketClient:
- Validate responsePayload shape before casting (guard against [uuid, null])
- Reject connect() Promise if socket closes before onopen fires
- Add tests for both edge cases
CLI:
- Validate ws:/wss: URL scheme in parseServerUrl
- Output ServerFailureError.payload via formatter (show hashIdsFailed details)
- Extract shared parseInteger() validator — reject NaN with clear error
- Remove dead error types (AuthenticationError, ServerError, TimeoutError)
- Chain build in test:integration script
- Remove unreachable FAILURE branch in outputTable
* ci: remove redundant lint:fix from autofix workflow
pnpm format already runs eslint --cache --fix, making the separate
pnpm lint:fix step redundant in all three ui workspaces.
* refactor(cli): remove unnecessary comment in outputTable
* fix(cli): don't override template defaults in station add, reject array config
- station add: only include autoStart, persistentConfiguration,
ocppStrictCompliance, deleteConfiguration in payload when explicitly
passed — lets server use template defaults otherwise
- config loader: reject uiServer array with clear error instead of
silently spreading array keys into object
* refactor(cli): remove unused terminal-link dep and dead exports
- Remove terminal-link from dependencies (never imported)
- Remove unused exports: printSuccess, printWarning, printInfo (human.ts),
outputTableList (table.ts)
- Remove corresponding test for printSuccess
* [autofix.ci] apply automated fixes
* fix(ui-common): reject malformed payloads, replace ReadyState with enum
- handleMessage: reject pending handler immediately when server sends
response with matching UUID but missing status field, instead of
silently dropping and waiting for 60s timeout
- Replace ReadyState type alias with WebSocketReadyState const enum
- Remove redundant ReadyState type (duplicated enum semantics)
* fix(cli): add connection timeout, remove dead defaultUIServerConfig
- Wrap client.connect() with Promise.race timeout to prevent infinite
hang when server accepts TCP but never completes WS handshake
- Remove unused defaultUIServerConfig export from defaults.ts
* fix(cli): clear connection timeout timer on success
- WebSocketClient: make buildUrl() a public url getter so consumers
use the canonical URL instead of reconstructing it
- lifecycle.ts: use client.url instead of building URL independently
- Remove ConfigurationType.ts: UIServerConfigurationSection is now
a type alias for the Zod-inferred UIServerConfig (single source)
* fix(cli): display failure status instead of misleading Success
displayGenericPayload now checks payload.status before printing — a
failure response without hashIds shows the red status line instead of
a green checkmark.
* fix: align ui/web ResponsePayload with server, reject non-object config
- ui/web ResponsePayload: replace incorrect hashIds with
hashIdsFailed/hashIdsSucceeded/responsesFailed matching server schema
- cli config loader: reject non-object uiServer values with clear error
instead of silently falling back to defaults
- Config loader: throw on primitive JSON config values (42, "hello")
instead of silently falling back to defaults
- Merge duplicate UIProtocol.ts import in ui/common/src/client/types.ts
* fix: suppress dangling connect rejection, remove dead Zod defaults
- Attach .catch() to connect() promise to prevent unhandled rejection
when the timeout wins Promise.race and disconnect triggers onclose
- Remove .default() calls from Zod schema — the CLI loader always
provides all fields via its canonical defaults map, making Zod
defaults dead code paths
* fix(cli): preserve error cause in config loader for debuggability
refactor(tests): replace re-export hub with direct imports
Remove ChargingStationTestUtils.ts re-export hub and update all 73
test files to import directly from their defining modules
(StationHelpers, TestLifecycleHelpers, MockCaches, MockWebSocket).
Fix __testable__ barrel bypass in OCPP20RequestService-CallChain test.
Harmonize Web UI test imports to use composables/types barrels
consistently and export validateUUID from composables barrel.
Add 'Direct Imports' convention to TEST_STYLE_GUIDE.md §6.
fix(ocpp): use Volatile instead of invented Ephemeral for CertificatePrivateKey persistence
Remove non-spec PersistenceEnumType.Ephemeral value. Use existing
Volatile which correctly prevents disk persistence and is already
used for ~25 other runtime-only variables in the registry.
refactor(crypto): modernize crypto APIs and harden certificate handling
- Migrate createHash() to crypto.hash() one-shot API (Node.js 22+)
- Migrate createSign() to crypto.sign() one-shot API
- Add derLength() bounds check for lengths > 0xFFFF
- Change CertificatePrivateKey persistence to Ephemeral
- Add logger.debug in deriveSigningMethodFromPublicKeyHex catch block
- Harden validateCertificatePath with async realpath for symlink
protection (falls back to resolve for not-yet-created paths)
- Add X.509 chain validation in validateCertificateX509: verify issuer
linkage and signatures for all certificates, validate validity
period for entire chain (not just leaf), document leaf-first ordering
- Implement RFC 6960 §4.1.1 compliant issuerNameHash via DER issuer
extraction from certificate raw bytes (extractDerIssuer)
- Move DER parsing functions (readDerLength, skipDerElement,
extractDerIssuer) to Asn1DerUtils with proper JSDoc
- Rename readDerTagLength to readDerLength for accuracy
- Add unit tests for DER parsing functions and chain validation
- Add real CA + leaf certificate test fixtures
refactor(ocpp): use Node.js crypto for EC curve derivation
Replace manual ASN.1 OID hex substring matching with
crypto.createPublicKey which parses the DER structure via OpenSSL,
validates the EC point, and returns the named curve directly. Map
Node.js curve names (e.g. prime256v1) to SigningMethodEnumType.
feat(ocpp): add signing prerequisites validation and EC curve auto-derivation
- Add deriveSigningMethodFromPublicKeyHex: extracts EC curve OID from
ASN.1 DER public key and maps to SigningMethodEnumType
- Add validateSigningPrerequisites: checks public key presence, curve
detection, and config/key curve consistency
- Integrate validation in OCPP 2.0 (OCPPServiceUtils.buildMeterValue)
and OCPP 1.6 (readSigningConfigForConnector) signing paths
- On validation failure: log warning and gracefully fallback to unsigned
meter values instead of producing invalid signed data
- Add unit tests for deriveSigningMethodFromPublicKeyHex and
validateSigningPrerequisites covering all paths
feat(ocpp): auto-derive signing method from EC public key curve
- Add deriveSigningMethodFromPublicKeyHex that extracts EC curve OID
from ASN.1 DER public key and maps to SigningMethodEnumType
- Add validateSigningPrerequisites that validates public key presence,
curve detection, and config consistency — returns enabled/disabled
with reason
- Integrate validation in OCPP 2.0 (OCPPServiceUtils.buildMeterValue)
and OCPP 1.6 (readSigningConfigForConnector) signing paths
- On validation failure: log error and gracefully fallback to unsigned
meter values instead of producing invalid signed data
- Move TEST_PUBLIC_KEY_HEX to ChargingStationTestConstants for reuse
- Update test fixtures to use valid secp256k1 ASN.1 EC public key