ADR-062: Centralized ID Management Integration
<!-- MADR 3.0 Template - Markdown Any Decision Records -->
Status
Proposed
Date
2025-12-18
Deciders
- Norbert Bede
Context and Problem Statement
When creating Application Dictionary elements (tables, columns, windows, processes, etc.) through iDempiere Hub (CLI, Chat API, or MCP Server), unique IDs must be assigned to prevent conflicts across distributed development teams. iDempiere's Centralized ID Management system provides a web service at developer.idempiere.com that coordinates ID allocation based on entity type registration.
Currently, iDempiere Hub generates AD elements without integrating with the centralized ID server, which means:
- Generated elements use manual/hardcoded IDs that may conflict
- No automatic registration with entity type owners
- Migration scripts may fail when deployed to environments with ID conflicts
- No support for custom implementor centralized servers
Each implementor should be able to configure their own centralized ID server for their custom entity types (e.g., XX_, YY_, ZZ_ prefixes).
Decision Drivers
- ID Conflict Prevention: Avoid duplicate IDs across distributed teams
- Entity Type Registration: Properly register elements based on entity type ownership
- Implementor Flexibility: Support custom centralized servers for implementors
- Tool Integration: Seamless integration across CLI, Chat API, and MCP interfaces
- Migration Safety: Generated migration scripts must use centrally-assigned IDs
- Developer Experience: Minimal configuration, automatic credential management
- Backwards Compatibility: Support both centralized and local-only modes
Considered Options
- No Integration - Continue using manual/sequential IDs
- Client-Side Integration - Hub directly calls centralized ID server
- iDempiere REST API Proxy - Delegate ID allocation to iDempiere's existing mechanism
- Hybrid Approach - Support both centralized and local modes with configuration
Decision Outcome
Chosen option: "Hybrid Approach", because it provides maximum flexibility for different deployment scenarios while maintaining compatibility with iDempiere's standard centralized ID mechanism.
Confirmation
- [ ] CLI
registry create-tableallocates IDs from centralized server when configured - [ ] Entity type registration validates against centralized server
- [ ] Migration scripts contain centrally-assigned IDs
- [ ] Custom implementor servers are supported via configuration
- [ ] Local mode (no centralized server) still works for development
- [ ] Credentials are securely managed (environment variables, not hardcoded)
Pros and Cons of the Options
Option 1: No Integration
Continue current approach with manual/hardcoded IDs.
- Good, because no development effort required
- Good, because no external dependencies
- Bad, because ID conflicts are likely in multi-team environments
- Bad, because not following iDempiere best practices
- Bad, because migration script deployment failures
Option 2: Client-Side Integration
iDempiere Hub directly calls centralized ID server REST API.
- Good, because full control over ID allocation logic
- Good, because works without iDempiere instance running
- Good, because can validate IDs before database insertion
- Bad, because duplicates logic already in iDempiere core
- Bad, because must maintain HTTP client and authentication
- Bad, because bypasses iDempiere's entity type validation
Option 3: iDempiere REST API Proxy
Delegate ID allocation to iDempiere's built-in mechanism via REST API.
- Good, because reuses proven iDempiere logic
- Good, because inherits entity type validation
- Good, because centralized ID credentials managed by iDempiere
- Bad, because requires running iDempiere instance
- Bad, because REST API may not expose ID allocation endpoints
- Bad, because tight coupling to iDempiere runtime
Option 4: Hybrid Approach (Chosen)
Support multiple modes: centralized (client-side HTTP), proxied (via iDempiere), or local (no server).
- Good, because flexible deployment options
- Good, because supports custom implementor servers
- Good, because graceful degradation when server unavailable
- Good, because clear configuration via environment variables
- Neutral, because requires mode selection configuration
- Bad, because more complex implementation (3 modes)
Architecture
Configuration
Smart Auto-Detection Strategy:
The system uses a 3-tier decision model:
-
Entity Type Detection (Primary):
U(System),D(Dictionary) → Always use local mode (no centralized needed)XX,YY,ZZ, etc. (Custom) → Require centralized mode or explicit override
-
Configuration Check (Secondary):
- If centralized credentials configured → Use centralized mode
- If
--local-idsflag present → Use local mode (development override) - If neither and custom entity type → Error with guidance
-
Interface-Specific Behavior (Tertiary):
- CLI: Can prompt user interactively if ambiguous
- MCP/Chat API: Must have configuration pre-set, cannot prompt
Environment Variables:
# Optional: Force specific mode (overrides auto-detection)
CENTRALIZED_ID_MODE=auto|centralized|proxied|local
# - auto: Smart detection based on entity type (default)
# - centralized: Always use centralized server
# - proxied: Always proxy via iDempiere REST API
# - local: Always use local sequential IDs (development only)
# Centralized mode credentials
CENTRALIZED_ID_SERVER_URL=https://developer.idempiere.com/cgi-bin/get_ID
CENTRALIZED_ID_PROJECT=Adempiere
CENTRALIZED_ID_USER=your_github_username
CENTRALIZED_ID_PASSWORD=your_assigned_password
# Proxied mode (via iDempiere REST API)
IDEMPIERE_URL=http://localhost:8080
IDEMPIERE_TOKEN=your_jwt_token
# Local mode - no configuration needed
Command-Line Flag (CLI only):
# Force local mode for development/testing
idempiere-hub registry create-table XX_TestTable --local-ids
# This bypasses centralized ID requirement (warning displayed)
ID Allocation Flow
┌─────────────────────────────────────────────────────────────────────┐
│ iDempiere Hub (CLI / Chat API / MCP Server) │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Generator: createTable, createColumn, createWindow, etc. │ │
│ └──────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼────────────────────────────────────┐ │
│ │ CentralizedIdService │ │
│ │ ├─ Mode detection (centralized/proxied/local) │ │
│ │ ├─ Entity type resolution (U=System, XX=Custom) │ │
│ │ └─ ID allocation strategy selection │ │
│ └──────────────────┬────────────┬──────────────┬────────────────┘ │
└────────────────────┼────────────┼──────────────┼──────────────────┘
│ │ │
┌─────────────▼──┐ ┌──────▼──────┐ ┌───▼────────┐
│ Centralized │ │ iDempiere │ │ Local │
│ HTTP Client │ │ REST Proxy │ │ Sequential │
└────────┬───────┘ └──────┬──────┘ └────────────┘
│ │
┌───────────▼─────────────────▼───────────────┐
│ developer.idempiere.com/cgi-bin/get_ID │
│ OR custom implementor server │
└─────────────────────────────────────────────┘
API Contract
public interface CentralizedIdService {
/**
* Allocate unique ID for AD table
* @param tableName AD table name (e.g., "AD_Column")
* @param entityType Entity type (e.g., "U", "XX")
* @param comment Description for audit trail
* @return Allocated ID
* @throws IdAllocationException if allocation fails
*/
int allocateId(String tableName, String entityType, String comment)
throws IdAllocationException;
/**
* Validate entity type against centralized registry
* @param entityType Entity type code
* @return true if registered to current user/project
*/
boolean validateEntityType(String entityType);
/**
* Check if centralized ID mode is enabled
*/
boolean isCentralizedMode();
/**
* Get current mode
*/
IdAllocationMode getMode();
}
enum IdAllocationMode {
LOCAL_SYSTEM, // System entity types (U, D) - always local
CENTRALIZED_REQUIRED, // Custom entity types (XX, YY, ZZ) - needs server
LOCAL_OVERRIDE // User forced local via --local-ids flag
}
enum IdAllocationStrategy {
CENTRALIZED, // Direct HTTP to centralized server
PROXIED, // Via iDempiere REST API
LOCAL // Sequential IDs (for LOCAL_SYSTEM or LOCAL_OVERRIDE modes)
}
HTTP Request Format (Centralized Mode)
GET /cgi-bin/get_ID?PROJECT=Adempiere&USER=username&PASSWORD=password&TABLE=AD_Column&COMMENT=XX_CustomField HTTP/1.1
Host: developer.idempiere.com
Response:
1000000
Supported AD Tables
Per Centralized ID Management wiki, the following 41+ tables are supported:
- AD_Column, AD_Element, AD_Field, AD_FieldGroup
- AD_Form, AD_InfoColumn, AD_InfoWindow, AD_Menu
- AD_Message, AD_Modification, AD_PrintFormat
- AD_PrintFormatItem, AD_Process, AD_Process_Para
- AD_Ref_List, AD_Reference, AD_ReportView
- AD_Role, AD_Tab, AD_Table, AD_Task
- AD_Val_Rule, AD_Window, AD_Workflow
- AD_WF_Node, AD_WF_Node_Para, AD_Workbench
- AD_WorkbenchWindow, C_AcctSchema_Element
- GL_Category, M_Warehouse, PA_ColorSchema
- PA_DashboardContent, PA_ReportCube, Plus more...
Entity Type Validation & Smart Detection
// Example: Creating table with XX_ prefix
String tableName = "XX_ProductRating";
String entityType = "XX"; // Custom implementor entity type
// 1. Auto-detect mode based on entity type
IdAllocationMode mode = centralizedIdService.detectMode(entityType);
switch (mode) {
case LOCAL_SYSTEM:
// System entity types (U, D) always use local IDs
logger.info("Using local ID allocation for system entity type: " + entityType);
return localIdGenerator.nextId("AD_Table");
case CENTRALIZED_REQUIRED:
// Custom entity types (XX, YY, ZZ, etc.) require centralized server
if (!centralizedIdService.isConfigured()) {
throw new CentralizedIdRequiredException(
"Entity type '" + entityType + "' requires centralized ID allocation.\n" +
"\n" +
"Options:\n" +
"1. Configure centralized ID server:\n" +
" export CENTRALIZED_ID_SERVER_URL=https://developer.idempiere.com/cgi-bin/get_ID\n" +
" export CENTRALIZED_ID_USER=your_github_username\n" +
" export CENTRALIZED_ID_PASSWORD=your_password\n" +
"\n" +
"2. Use local IDs for development (NOT for production):\n" +
" idempiere-hub registry create-table " + tableName + " --local-ids\n" +
"\n" +
"3. Get credentials at: https://groups.google.com/g/idempiere"
);
}
// Validate entity type is registered
if (!centralizedIdService.validateEntityType(entityType)) {
throw new EntityTypeNotRegisteredException(
"Entity type '" + entityType + "' not registered to user: " +
centralizedIdService.getCurrentUser() + "\n" +
"Register at: " + centralizedIdService.getServerUrl().replace("/get_ID", "")
);
}
// Allocate from centralized server
return centralizedIdService.allocateId("AD_Table", entityType,
"Custom Product Rating Table");
case LOCAL_OVERRIDE:
// User explicitly requested local mode via --local-ids flag
logger.warn("⚠️ Using LOCAL ID allocation for custom entity type: " + entityType);
logger.warn("⚠️ This is for DEVELOPMENT ONLY - do not deploy to production!");
return localIdGenerator.nextId("AD_Table");
}
Migration Script Generation
Generated migration scripts must include centrally-assigned IDs:
-- Generated by iDempiere Hub with Centralized ID allocation
-- Entity Type: XX
-- User: johndoe
-- Date: 2025-12-18
-- ID 1000000 allocated from developer.idempiere.com
INSERT INTO AD_Table (AD_Table_ID, Name, TableName, EntityType, ...)
VALUES (1000000, 'Product Rating', 'XX_ProductRating', 'XX', ...);
-- ID 1000001 allocated from developer.idempiere.com
INSERT INTO AD_Column (AD_Column_ID, AD_Table_ID, ColumnName, EntityType, ...)
VALUES (1000001, 1000000, 'XX_ProductRating_ID', 'XX', ...);
Usage Examples by Scenario
Scenario 1: System Developer (U entity type)
Use Case: Core iDempiere contributor adding a system table
# No centralized config needed - auto-detects U entity type
idempiere-hub registry create-table C_PaymentMethod --entity-type U
# Output:
# ✅ Using local ID allocation for system entity type: U
# Created AD_Table with ID: 200001 (local sequence)
MCP/Chat API:
User: "Create a system table C_PaymentMethod"
Claude: [Uses create-table tool, auto-detects entity type U, no config needed]
Scenario 2: Plugin Developer (XX entity type) - Configured
Use Case: Developing XX_ProductRating for production deployment
# Configure centralized ID server (one-time setup)
export CENTRALIZED_ID_SERVER_URL=https://developer.idempiere.com/cgi-bin/get_ID
export CENTRALIZED_ID_USER=johndoe
export CENTRALIZED_ID_PASSWORD=secret123
export CENTRALIZED_ID_PROJECT=Adempiere
# Create table - automatically uses centralized IDs
idempiere-hub registry create-table XX_ProductRating --entity-type XX
# Output:
# 🌐 Connecting to centralized ID server...
# ✅ Allocated AD_Table ID: 1000500 from developer.idempiere.com
# ✅ Allocated AD_Column IDs: 1000501-1000510
# 📝 Migration script: migrations/XX_ProductRating.sql
MCP/Chat API:
# Pre-configured via environment variables
User: "Create a product rating table with XX entity type"
Claude: [Uses create-table tool, detects XX entity type, uses centralized server]
Scenario 3: Plugin Developer (XX entity type) - NOT Configured
Use Case: Quick local testing without centralized server
# Try to create without config
idempiere-hub registry create-table XX_TestTable --entity-type XX
# Output:
# ❌ Error: Entity type 'XX' requires centralized ID allocation.
#
# Options:
# 1. Configure centralized ID server:
# export CENTRALIZED_ID_SERVER_URL=https://developer.idempiere.com/cgi-bin/get_ID
# export CENTRALIZED_ID_USER=your_github_username
# export CENTRALIZED_ID_PASSWORD=your_password
#
# 2. Use local IDs for development (NOT for production):
# idempiere-hub registry create-table XX_TestTable --local-ids
#
# 3. Get credentials at: https://groups.google.com/g/idempiere
# Use local override for development
idempiere-hub registry create-table XX_TestTable --entity-type XX --local-ids
# Output:
# ⚠️ Using LOCAL ID allocation for custom entity type: XX
# ⚠️ This is for DEVELOPMENT ONLY - do not deploy to production!
# Created AD_Table with ID: 200002 (local sequence)
MCP/Chat API:
User: "Create XX_TestTable"
Claude: "I cannot create this table because entity type XX requires centralized ID allocation,
but no centralized ID server is configured.
Would you like to:
1. Configure the centralized ID server (I can guide you)
2. Use local IDs for development testing only (not for production)"
Scenario 4: Implementor with Custom Server (YY entity type)
Use Case: Company with their own centralized ID server
# Configure custom implementor server
export CENTRALIZED_ID_MODE=centralized
export CENTRALIZED_ID_SERVER_URL=https://ids.mycompany.com/cgi-bin/get_ID
export CENTRALIZED_ID_USER=employee1
export CENTRALIZED_ID_PASSWORD=companypass
export CENTRALIZED_ID_PROJECT=MyCompanyERP
# Create table
idempiere-hub registry create-table YY_CustomFeature --entity-type YY
# Output:
# 🌐 Connecting to custom ID server: ids.mycompany.com
# ✅ Allocated AD_Table ID: 2000100 from ids.mycompany.com
Scenario 5: CI/CD Pipeline (Validation Only)
Use Case: CI pipeline should validate without allocating real IDs
# Force local mode for CI testing
export CENTRALIZED_ID_MODE=local
# Run tests/validation
idempiere-hub registry create-table XX_TestTable --entity-type XX
# Output:
# ⚠️ CENTRALIZED_ID_MODE=local - using local IDs
# ⚠️ This build is NOT production-ready
# Created AD_Table with ID: 200003 (local sequence)
Scenario 6: MCP Server Long-Running Session
Use Case: MCP server running for hours, token might expire
# Start MCP server with centralized ID config
export CENTRALIZED_ID_SERVER_URL=https://developer.idempiere.com/cgi-bin/get_ID
export CENTRALIZED_ID_USER=johndoe
export CENTRALIZED_ID_PASSWORD=secret123
java -Dquarkus.profile=mcp -jar target/idempiere-hub-runner.jar server mcp
In Claude Desktop:
User (8am): "Create table XX_Morning with entity type XX"
Claude: ✅ Created with centralized ID 1000600
User (2pm): "Create table XX_Afternoon with entity type XX"
Claude: ✅ Created with centralized ID 1000650
# (No token expiry issues - credentials stored in env vars)
User (5pm): "Create table C_SystemTable with entity type U"
Claude: ✅ Created with local ID 200004
# (System entity type, no centralized server call needed)
Implementation Plan
Phase 1: Core Infrastructure (1-2 weeks)
- [ ] Create
CentralizedIdServiceinterface and implementations - [ ] Implement HTTP client for centralized server communication
- [ ] Add configuration via environment variables
- [ ] Create
IdAllocationModedetection logic - [ ] Add entity type validation
Phase 2: Generator Integration (1 week)
- [ ] Update
TableToolLogic.createTable()to use centralized IDs - [ ] Update
GeneratorToolLogicfor plugins/models/processes - [ ] Modify migration script generation to include allocated IDs
- [ ] Add ID allocation audit logging
Phase 3: Custom Server Support (1 week)
- [ ] Support custom
CENTRALIZED_ID_SERVER_URLfor implementors - [ ] Add server health check and fallback logic
- [ ] Document implementor server setup (CGI scripts from idempiere-stuff)
- [ ] Create configuration templates for common scenarios
Phase 4: Testing & Documentation (1 week)
- [ ] Unit tests for all 3 modes (centralized/proxied/local)
- [ ] Integration tests with developer.idempiere.com
- [ ] Update USER_GUIDE.md with centralized ID configuration
- [ ] Add troubleshooting guide for ID conflicts
- [ ] Document custom server deployment
Error Handling
// Example error scenarios
// 1. Server unreachable
try {
int id = centralizedIdService.allocateId("AD_Table", "XX", "Test");
} catch (IdAllocationException e) {
if (e.getCause() instanceof ConnectException) {
// Fallback to local mode or prompt user
logger.warn("Centralized ID server unreachable, falling back to local mode");
return localIdGenerator.nextId("AD_Table");
}
}
// 2. Invalid credentials
catch (IdAllocationException e) {
if (e.getMessage().contains("401") || e.getMessage().contains("403")) {
throw new IllegalStateException(
"Invalid centralized ID credentials. " +
"Check CENTRALIZED_ID_USER and CENTRALIZED_ID_PASSWORD environment variables."
);
}
}
// 3. Entity type not registered
catch (EntityTypeNotRegisteredException e) {
throw new IllegalArgumentException(
"Entity type '" + entityType + "' not registered. " +
"Register at: " + serverUrl.replace("/get_ID", "") +
"\nOr contact your system administrator."
);
}
Security Considerations
-
Credential Management
- Store credentials in environment variables, not in code
- Support credential files for CI/CD (
.idempiere-credentials) - Warn when using local mode in production
-
HTTPS Required
- Enforce HTTPS for centralized server communication
- Validate SSL certificates
- Support custom CA certificates for private servers
-
Audit Trail
- Log all ID allocations with timestamp, user, table, comment
- Include allocation source (centralized/proxied/local) in audit
- Generate allocation report for migration review
Custom Implementor Server Setup
For organizations wanting their own centralized ID server:
1. Deploy CGI Scripts
# Clone idempiere-stuff repository
git clone https://github.com/idempiere/idempiere-stuff.git
# Deploy CGI scripts to web server
cd idempiere-stuff/org.idempiere.webstore/WEB-INF/cgi-bin
cp get_ID.cgi /var/www/cgi-bin/
chmod +x /var/www/cgi-bin/get_ID.cgi
2. Configure Database
-- Create ID allocation tracking table
CREATE TABLE id_allocation (
id SERIAL PRIMARY KEY,
project VARCHAR(50),
username VARCHAR(50),
tablename VARCHAR(50),
allocated_id INTEGER,
comment TEXT,
created TIMESTAMP DEFAULT NOW()
);
3. Configure iDempiere Hub
# Point to custom server
export CENTRALIZED_ID_MODE=centralized
export CENTRALIZED_ID_SERVER_URL=https://ids.yourcompany.com/cgi-bin/get_ID
export CENTRALIZED_ID_PROJECT=YourProject
export CENTRALIZED_ID_USER=youruser
export CENTRALIZED_ID_PASSWORD=yourpassword
More Information
Practical Flow Guide
👉 See 062-practical-flow.md for detailed scenarios:
- When does it trigger?
- Day-by-day plugin development lifecycle
- Team collaboration examples
- MCP Server usage patterns
- CI/CD pipeline integration
- Troubleshooting guide
Related ADRs
- ADR-004: Enhanced Table Creation - Table creation logic
- ADR-005: Migration Script Architecture - Migration generation
- ADR-008: Application Dictionary Registry - AD metadata access
- ADR-054: AI Tool Architecture Clarity - Tool integration patterns
References
- iDempiere Wiki: Centralized ID Management
- iDempiere Stuff Repository: CGI Scripts
- developer.idempiere.com - Official centralized ID server
- iDempiere Forums: Getting Committer Password
Future Enhancements
- ID Range Reservation: Reserve ID ranges for batch operations
- Offline Mode: Cache ID ranges for offline development
- Conflict Resolution: Detect and resolve ID conflicts in existing installations
- Entity Type Registry UI: Web interface for entity type registration
- Multi-Project Support: Single server managing multiple project namespaces
Future: CloudEmpiere Internal ID Management (ADR-063)
The current implementation uses HTTP client for community server communication. For CloudEmpiere internal plugins, we plan to integrate with a generic Hub Storage Adapter (ADR-063):
┌─────────────────────────────────────────────────────────────────┐
│ CentralizedIdService │
│ ├─ Scope Detection: CE_* → internal, XX_* → check registry │
│ └─ Uses: StorageAdapter OR HTTP Client │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────┴─────────────────────┐
│ │
┌───────▼───────┐ ┌─────────▼─────────┐
│ StorageAdapter│ │ HTTP Client │
│ (ADR-063) │ │ (Community) │
│ │ │ │
│ CloudEmpiere │ │ developer. │
│ internal IDs │ │ idempiere.com │
└───────────────┘ └───────────────────┘
Scope Decision Flow:
CE_*entity types → CloudEmpiere StorageAdapter (internal)U,Dentity types → Local sequential IDs- Other custom types → Check entity type registry, route accordingly
This enables:
- No external dependency for CloudEmpiere-specific plugins
- Flexible storage backends via ADR-063 adapters (PostgreSQL, S3, DynamoDB)
- Dev/Prod environments with different storage configurations
Note: This ADR focuses on ID allocation mechanism. Actual table/element creation logic remains in existing services (TableToolLogic, GeneratorToolLogic). This is a cross-cutting concern that enhances all AD element generation tools.