Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions docs/docs/deployment/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,28 @@ Here are the configuration keys, for both containers (environment variables) and

#### Basic parameters

| Parameter | Environment variable | Default value | Description |
|:--------------------------------------------|:--------------------------------------------|:----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| server.address | SERVER_ADDRESS | 0.0.0.0 | Listen address of the application |
| server.port | SERVER_PORT | 8080 | Listen port of the application |
| openaev.base-url | OPENAEV_BASE-URL | http://localhost:8080 | Base URL of the application, used for some email links and as the default agent URL (agent installer scripts and executor commands) unless `openaev.agent-url` is set. In production environments, ensure this URL can be resolved from endpoints where agents will be deployed. |
| server.servlet.session.timeout | SERVER_SERVLET_SESSION_TIMEOUT | 1440m | Rolling session timeout: every request extends the session by this duration. Sessions are persisted in PostgreSQL and survive platform restarts |
| openaev.session-idle-timeout | OPENAEV_SESSION-IDLE-TIMEOUT | 0 | Idle time before the UI locks the screen and asks the user to continue or log out (0 = disabled, e.g. 30m). Must be lower than the session timeout |
| openaev.session-cookie | OPENAEV_SESSION-COOKIE | `false` | When `true`, the session cookie dies when the browser closes (server-side timeout still applies). When `false`, users stay logged in across browser restarts: the cookie is re-issued on every request (sliding Max-Age), so it only expires after `openaev.cookie-duration` of inactivity |
| openaev.cookie-secure | OPENAEV_COOKIE-SECURE | `false` | Turn on if the access is done in HTTPS |
| openaev.cookie-duration | OPENAEV_COOKIE-DURATION | P1D | Cookie validity sliding window (default 1 day). Each request re-issues the cookie with this Max-Age, so active users are never logged out by cookie expiration |
| openaev.admin.email | OPENAEV_ADMIN_EMAIL | admin@openaev.io | Default login email of the admin user |
| openaev.admin.password | OPENAEV_ADMIN_PASSWORD | ChangeMe | Default password of the admin user |
| openaev.admin.token | OPENAEV_ADMIN_TOKEN | ChangeMe | Default token (must be a valid UUIDv4) |
| openaev.admin.encryption_key | OPENAEV_ADMIN_ENCRYPTION_KEY | ChangeMe | Encryption key used for encrypting sensitive data in database. Encryption key and salt are used to generate a 256bit encryption key for encrypting purpose. |
| openaev.admin.encryption_salt | OPENAEV_ADMIN_ENCRYPTION_SALT | ChangeMe | Encryption salt used for encrypting sensitive data in database. Must be at least 8 bytes long. Encryption key and salt are used to generate a 256bit encryption key for encrypting purpose |
| openaev.healthcheck.key | OPENAEV_HEALTHCHECK_KEY | ChangeMe | The key to use in the health check endpoint (/api/health) |
| inject.execution.threshold.minutes | INJECT_EXECUTION_THRESHOLD_MINUTES | 10 | Inject execution threshold in minutes. If this time is exceeded, the inject will be moved to the MAYBE_PREVENTED status. |
| openaev.run-mode | OPENAEV_RUN-MODE | normal | Startup run mode (`normal` or `safe`). In `safe`, Quartz background processing is disabled. See [Run modes](platform/run-modes.md). |
| openaev.starterpack.enabled | OPENAEV_STARTERPACK_ENABLED | true | StarterPack feature, providing default endpoint, asset group, scenarios and dashboards |
| openaev.url.access.token.expiry-margin-days | OPENAEV_URL_ACCESS_TOKEN_EXPIRY-MARGIN-DAYS | 7 | Number of days added after an exercise end date before URL access tokens expire |
| openaev.url.access.token.retention-days | OPENAEV_URL_ACCESS_TOKEN_RETENTION-DAYS | 30 | Number of days to retain expired or revoked URL access tokens before the purge job deletes them |
| Parameter | Environment variable | Default value | Description |
|:------------------------------------------------------|:------------------------------------------------------|:----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| server.address | SERVER_ADDRESS | 0.0.0.0 | Listen address of the application |
| server.port | SERVER_PORT | 8080 | Listen port of the application |
| openaev.base-url | OPENAEV_BASE-URL | http://localhost:8080 | Base URL of the application, used for some email links and as the default agent URL (agent installer scripts and executor commands) unless `openaev.agent-url` is set. In production environments, ensure this URL can be resolved from endpoints where agents will be deployed. |
| server.servlet.session.timeout | SERVER_SERVLET_SESSION_TIMEOUT | 1440m | Rolling session timeout: every request extends the session by this duration. Sessions are persisted in PostgreSQL and survive platform restarts |
| openaev.session-idle-timeout | OPENAEV_SESSION-IDLE-TIMEOUT | 0 | Idle time before the UI locks the screen and asks the user to continue or log out (0 = disabled, e.g. 30m). Must be lower than the session timeout |
| openaev.session-cookie | OPENAEV_SESSION-COOKIE | `false` | When `true`, the session cookie dies when the browser closes (server-side timeout still applies). When `false`, users stay logged in across browser restarts: the cookie is re-issued on every request (sliding Max-Age), so it only expires after `openaev.cookie-duration` of inactivity |
| openaev.cookie-secure | OPENAEV_COOKIE-SECURE | `false` | Turn on if the access is done in HTTPS |
| openaev.cookie-duration | OPENAEV_COOKIE-DURATION | P1D | Cookie validity sliding window (default 1 day). Each request re-issues the cookie with this Max-Age, so active users are never logged out by cookie expiration |
| openaev.admin.email | OPENAEV_ADMIN_EMAIL | admin@openaev.io | Default login email of the admin user |
| openaev.admin.password | OPENAEV_ADMIN_PASSWORD | ChangeMe | Default password of the admin user |
| openaev.admin.token | OPENAEV_ADMIN_TOKEN | ChangeMe | Default token (must be a valid UUIDv4) |
| openaev.admin.encryption_key | OPENAEV_ADMIN_ENCRYPTION_KEY | ChangeMe | Encryption key used for encrypting sensitive data in database. Encryption key and salt are used to generate a 256bit encryption key for encrypting purpose. |
| openaev.admin.encryption_salt | OPENAEV_ADMIN_ENCRYPTION_SALT | ChangeMe | Encryption salt used for encrypting sensitive data in database. Must be at least 8 bytes long. Encryption key and salt are used to generate a 256bit encryption key for encrypting purpose |
| openaev.healthcheck.key | OPENAEV_HEALTHCHECK_KEY | ChangeMe | The key to use in the health check endpoint (/api/health) |
| inject.execution.threshold.minutes | INJECT_EXECUTION_THRESHOLD_MINUTES | 10 | Inject execution threshold in minutes. If this time is exceeded, the inject will be moved to the MAYBE_PREVENTED status. |
| openaev.cron.config.agent.inactivity.monitor.interval | OPENAEV_CRON_CONFIG_AGENT_INACTIVITY_MONITOR_INTERVAL | 5 | Polling interval in minutes for the agent inactivity monitor job. |
| openaev.run-mode | OPENAEV_RUN-MODE | normal | Startup run mode (`normal` or `safe`). In `safe`, Quartz background processing is disabled. See [Run modes](platform/run-modes.md). |
| openaev.starterpack.enabled | OPENAEV_STARTERPACK_ENABLED | true | StarterPack feature, providing default endpoint, asset group, scenarios and dashboards |
| openaev.url.access.token.expiry-margin-days | OPENAEV_URL_ACCESS_TOKEN_EXPIRY-MARGIN-DAYS | 7 | Number of days added after an exercise end date before URL access tokens expire |
| openaev.url.access.token.retention-days | OPENAEV_URL_ACCESS_TOKEN_RETENTION-DAYS | 30 | Number of days to retain expired or revoked URL access tokens before the purge job deletes them |

#### Network and security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ private void setUpdatedAgentAttributes(Agent agent, AgentRegisterInput input, En
agent.setAsset(endpoint);
agent.setProcessName(input.getProcessName());
agent.setLastSeen(input.getLastSeen());
agent.setStatus(AgentStatus.ACTIVE);
agent.setExternalReference(input.getExternalReference());
clearAbilityForAgent(agent);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package io.openaev.executors.model;

import static io.openaev.database.model.Agent.ACTIVE_THRESHOLD_MILLIS;

import io.openaev.database.model.Endpoint;
import io.openaev.database.model.Executor;
import io.openaev.helper.AgentHelper;
import io.openaev.utils.mapper.EndpointMapper;
import java.time.Instant;
import lombok.Data;
Expand Down Expand Up @@ -42,6 +43,8 @@ public void setHostname(String hostname) {
}

public boolean isActive() {
return new AgentHelper().isAgentActiveFromLastSeen(this.getLastSeen());
return this.getLastSeen() != null
&& (Instant.now().toEpochMilli() - this.getLastSeen().toEpochMilli())
< ACTIVE_THRESHOLD_MILLIS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.openaev.migration;

import java.sql.Statement;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import org.springframework.stereotype.Component;

@Component
public class V6_20260818200000000__Add_status_to_agents extends BaseJavaMigration {

@Override
public void migrate(Context context) throws Exception {
try (Statement statement = context.getConnection().createStatement()) {
statement.execute(
"ALTER TABLE agents ADD COLUMN IF NOT EXISTS agent_status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE';");
statement.execute(
"UPDATE agents SET agent_status = 'INACTIVE'"
+ " WHERE agent_last_seen IS NULL"
+ " OR agent_last_seen < NOW() - INTERVAL '1 hour';");
statement.execute("DROP INDEX IF EXISTS idx_agents_status_last_seen;");
statement.execute(
"CREATE INDEX IF NOT EXISTS idx_agents_status_last_seen"
+ " ON agents(tenant_id, agent_status, agent_last_seen);");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.openaev.scheduler;

import static io.openaev.scheduler.jobs.AgentInactivityMonitorJob.AGENT_INACTIVITY_MONITOR_JOB;
import static io.openaev.scheduler.jobs.EngineDeletionReplayJob.ENGINE_DELETION_REPLAY_JOB;
import static io.openaev.scheduler.jobs.ExecutionTraceRetentionJob.EXECUTION_TRACE_RETENTION_JOB;
import static io.openaev.scheduler.jobs.TenantPurgeJob.TENANT_PURGE_JOB;
Expand Down Expand Up @@ -79,6 +80,14 @@ public JobDetail getConnectorPingJob() {
.build();
}

@Bean
public JobDetail agentInactivityMonitorJobDetail() {
return JobBuilder.newJob(AgentInactivityMonitorJob.class)
.withIdentity(AGENT_INACTIVITY_MONITOR_JOB)
.storeDurably()
.build();
}

@Bean
public JobDetail userEventRetentionJobDetail() {
return JobBuilder.newJob(UserEventRetentionJob.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.openaev.scheduler;

import static io.openaev.scheduler.jobs.AgentInactivityMonitorJob.AGENT_INACTIVITY_MONITOR_TRIGGER;
import static io.openaev.scheduler.jobs.EngineDeletionReplayJob.ENGINE_DELETION_REPLAY_TRIGGER;
import static io.openaev.scheduler.jobs.ExecutionTraceRetentionJob.EXECUTION_TRACE_RETENTION_TRIGGER;
import static io.openaev.scheduler.jobs.TenantPurgeJob.TENANT_PURGE_TRIGGER;
Expand Down Expand Up @@ -31,6 +32,9 @@ public class PlatformTriggers {
@Value("${openaev.cron.config.steps.delay.queue.polling.interval:10000}")
private int stepDelayQueue;

@Value("${openaev.cron.config.agent.inactivity.monitor.interval:5}")
private int agentInactivityMonitorIntervalMinutes;

@Autowired
public void setPlatformJobs(PlatformJobDefinitions platformJobs) {
this.platformJobs = platformJobs;
Expand Down Expand Up @@ -107,6 +111,19 @@ public Trigger connectorPingTrigger() {
.build();
}

@Bean
@Profile("!test")
public Trigger agentInactivityMonitorTrigger() {
return newTrigger()
.forJob(this.platformJobs.agentInactivityMonitorJobDetail())
.withIdentity(AGENT_INACTIVITY_MONITOR_TRIGGER)
.withSchedule(
simpleSchedule()
.withIntervalInMinutes(agentInactivityMonitorIntervalMinutes)
.repeatForever())
.build();
}

@Bean
public Trigger userEventRetentionTrigger() {
return newTrigger()
Expand Down
Loading
Loading