Objects
Generated from the record definitions in core/store. Edit the code, not this file.
The shapes the API returns. Every field a record carries is listed, including the ones an individual endpoint leaves null.
Agent
Agent is one host running the CertPilot agent. The credential is a public key. The agent generated the pair on its own host during enrolment and has never sent the private half anywhere, so this record — and the whole database it sits in — holds nothing that could impersonate it. That is the same argument that makes local key generation the point of the agent at all; the identity key is simply the first key CertPilot never sees.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
hostname | string | |
platform | string | |
version | string | |
public_key | string | PublicKey is PEM. Returned by the API on purpose: an operator comparing it against what the agent printed on the host is how "is the thing enrolled under this name the machine I ran the command on" gets answered. |
key_id | string | |
status | string | |
labels | object | |
enrol_token_id | string | |
enrolled_at | timestamp | |
enrolled_from | string | |
last_seen_at | timestamp | |
last_seen_ip | string | |
heartbeat_interval_seconds | integer | HeartbeatIntervalSeconds is what this agent said it would report at. Staleness is measured against that rather than one global number that is wrong for every agent configured differently. |
stale_alerted_at | timestamp | |
last_inventory_at | timestamp | LastInventoryAt is when this host last managed to report what is on it. Distinct from LastSeenAt for the reason every "last synced" column in this schema is: a host that has not been scanned since March must not read as a host with nothing on it. |
certificates_seen | integer | |
unmanaged_seen | integer | |
revoked_at | timestamp | |
revoked_by | string | |
created_at | timestamp | |
updated_at | timestamp |
AgentCertificate
AgentCertificate is one certificate file found on one host. The fourth place certificates hide, after served, issued, and stored in a cloud: a file on a disk. And the only one where the observer is running on the machine, which is what makes the two fields nobody else can produce possible — the private key's permissions, and whether it matches. There is no private key here, and no field one could travel in.
| Field | Type | Description |
|---|---|---|
id | string | |
agent_id | string | |
path | string | |
kind | string | Kind is leaf, ca, or bundle. A trust store is recorded as one row that says so rather than as a hundred findings about roots the distribution manages. |
certificate_count | integer | |
common_name | string | |
subject_dn | string | |
issuer_dn | string | |
serial_number | string | |
sans | string[] | |
not_before | timestamp | |
not_after | timestamp | |
key_type | string | |
key_size | integer | |
fingerprint_sha256 | string | |
certificate_pem | string | |
file_mode | string | What only a process on the host can see. |
file_owner | string | |
modified_at | timestamp | |
private_key_path | string | |
private_key_mode | string | |
private_key_in_same_file | boolean | |
private_key_matches | boolean | |
referenced_by | string[] | ReferencedBy is which server configurations name this file. Found by text search, so an empty list means "not matched", not "nothing uses this". |
management_state | string | |
matched_certificate_id | string | |
findings | Finding[] | |
agent_name | string | AgentName is joined in for listings that span hosts, so a page of findings does not need one lookup per row to say where they are. |
first_seen_at | timestamp | |
last_seen_at | timestamp | |
removed_at | timestamp | |
created_at | timestamp |
AgentEnrolToken
AgentEnrolToken is a credential handed to a machine that has never spoken to CertPilot. Deliberately not the same credential the agent uses afterwards. A bootstrap secret and an operating secret have different blast radii, and a long-lived shared enrolment token pasted into a configuration management template is a credential in a git repository.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
expires_at | timestamp | |
max_uses | integer | |
uses | integer | |
labels | object | |
revoked_at | timestamp | |
revoked_by | string | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
AgentGrant
AgentGrant is what a host is allowed to ask for. The security question that matters, and getting it wrong is worse than not having an agent at all. A credential that can request any name is a way to obtain a certificate for the payroll system from a compromised web server, signed by the organisation's own CA, looking exactly like every other issuance in the log.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
agent_id | string | AgentID targets one host. LabelSelector targets every agent carrying these labels — which come from the enrolment token rather than from the agent, so a host cannot label itself into somebody else's grant. |
label_selector | object | |
names | string[] | Names permitted, as exact hostnames or single-level wildcards. |
ca_account_id | string | CAAccountID is part of the grant rather than chosen by the agent: one that could pick its own issuer could pick the cheapest, the least logged, or the one with the widest trust. |
min_key_size | integer | |
allowed_key_types | string[] | |
validity_days | integer | |
renew_before_days | integer | |
is_enabled | boolean | |
revoked_at | timestamp | |
revoked_by | string | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
AgentInstallation
AgentInstallation is one named place on one host that a certificate goes. The host's own view, kept beside the central one rather than instead of it. CertificateDeployment answers "what is this certificate's state at that place" in the same shape as every other target type, which is what makes an agent a deployment target like any other. This answers a question that only exists for agents: this machine has been configured to install a certificate nobody granted it, and nothing else in the system can see that. There is no command field this can be written from. ReloadCommand arrives from the host for display and travels in one direction only; a core that could set it would be a fleet-wide remote execution channel with a certificate manager on the front.
| Field | Type | Description |
|---|---|---|
id | string | |
agent_id | string | |
name | string | Name is the destination's name in the host's spec — "nginx", "haproxy". |
certificate_name | string | CertificateName is the name the spec asks for, as written. Kept even when nothing matched it, because the unmatched string is the finding. |
certificate_id | string | |
fingerprint_sha256 | string | |
not_after | timestamp | |
paths | string[] | Paths are the files this destination writes, in the order it writes them. |
status | string | |
detail | string | |
last_error | string | |
rolled_back | boolean | RolledBack records that a failed attempt put the previous material back. Separate from the error: an install that failed and restored what was working is an inconvenience, one that did not is an outage, and a single status cannot say which. |
installed_at | timestamp | |
reloaded_at | timestamp | |
reload_command | string | |
check_command | string | |
agent_name | string | AgentName and Hostname are joined in for listings, so a page of what the fleet has installed does not issue one lookup per row. |
hostname | string | |
reported_at | timestamp | |
created_at | timestamp | |
updated_at | timestamp |
AlertAcknowledgement
AlertAcknowledgement records that a human has seen an alert and, optionally, that delivery should stay quiet for a while. The rule this type exists to enforce: silencing suppresses delivery, never display. An acknowledged CA still appears on the dashboard and in the wall view, marked as acknowledged and by whom. Hiding a problem because someone clicked a button is how CAs expire in organisations that believed they were monitoring them.
| Field | Type | Description |
|---|---|---|
id | string | |
entity_type | string | |
entity_id | string | |
threshold | integer | Threshold is the expiry threshold, in days, that this acknowledgement covers. Nil means it is not tied to one. This is the field that makes acknowledgement safe rather than dangerous. Acknowledging a CA at 30 days must not silence its 7-day alert: the situation has materially worsened, and the earlier "yes, we know" was an answer to a different question. |
acknowledged_by | string | |
acknowledged_by_email | string | |
acknowledged_at | timestamp | |
note | string | |
silence_until | timestamp | |
revoked_at | timestamp | |
revoked_by | string | |
created_at | timestamp |
AuditChainReport
AuditChainReport is the result of walking the chain.
| Field | Type | Description |
|---|---|---|
intact | boolean | Intact is the headline answer, and is false whenever the walk could not prove otherwise — including when there is no key to check with. |
verified | integer | Verified counts entries whose tags recomputed correctly. |
unchained | integer | Unchained counts entries written before tamper-evidence existed. They are reported rather than ignored: an operator reading "intact" is entitled to know how much of the record that claim actually covers. |
first_seq | integer | |
last_seq | integer | |
broken_at | integer | BrokenAt is the sequence number of the first entry that failed, or of the gap that was found where an entry should have been. |
reason | string | Reason is written for whoever reads this at 2am, and names the cause. |
checked_at | timestamp | |
truncated | boolean | Truncated reports that the walk stopped at its limit rather than at the end of the chain, so Intact covers only what was examined. |
AuditLog
AuditLog represents an immutable audit log entry.
| Field | Type | Description |
|---|---|---|
id | string | |
action | string | |
entity_type | string | |
entity_id | string | |
actor_id | string | |
actor_email | string | |
details | string | JSON string |
ip_address | string | |
created_at | timestamp | |
seq | integer | Seq is the entry's position in the tamper-evidence chain, assigned by the store. Gapless, so a deleted entry shows up as a hole rather than simply not being there. Zero for entries written before the chain existed. |
entry_hash | byte[] | EntryHash is this entry's keyed tag; PrevHash is the tag of the entry before it. PrevHash is not serialised because it is only meaningful while walking the chain, and a caller that wants to check the record should ask the verifier rather than reassemble the walk itself. |
AuthConfigResponse
AuthConfigResponse tells the frontend how to authenticate against this instance. Served rather than compiled in, so that one deployment is described by one configuration file. A frontend carrying its own build-time copy of the issuer and client id can disagree with the core it is talking to, and the way that disagreement presents is a login that appears to succeed followed by 401 on every subsequent request — which reads as a broken API rather than as a mismatched setting. Everything here is public by construction. In authorization code with PKCE there is no client secret: the issuer and client id are sent to the provider in a URL the user can read.
| Field | Type | Description |
|---|---|---|
mode | string | Mode is "oidc" when single sign-on is configured, otherwise "password". There is no longer an "anonymous" mode: an instance that nobody has to sign in to was a development convenience that made the authorisation paths the least exercised code in the system, and it attributed every action in the audit log to a subject nobody could be asked about. |
password_login | boolean | PasswordLogin is true whenever local accounts can be used, which is always — single sign-on is offered alongside it, never instead of it, so that a provider outage does not lock a team out of its own CA hierarchy. |
issuer | string | |
client_id | string | |
scopes | string[] | |
audience | string |
CAAccount
CAAccount represents a Gateway Connection configuration.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
provider_type | string | acme, vault, selfsigned, etc. |
gateway_addr | string | |
is_default | boolean | |
status | string | CONNECTED, DISCONNECTED, ERROR |
renewal_rate_limit | integer | RenewalRateLimit is how many certificates this account may successfully renew inside RenewalRateWindowHours. Zero means unlimited. Unlimited is the right default. Inventing a conservative limit for a CA whose real limits nobody has entered would delay renewals for a constraint that does not exist, and a certificate that expired because this tool was being cautious is the worst outcome available. |
renewal_rate_window_hours | integer | RenewalRateWindowHours is the rolling window the limit is counted over. Hours rather than a named period, because providers do not agree on what a period is — a rolling week for certificates, a rolling three hours for orders — and hours is the only shape that describes all of them without lying about any of them. |
last_health_at | timestamp | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
CAAuthority
CAAuthority represents a Certificate Authority record.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
ca_type | string | ROOT, INTERMEDIATE, ISSUING |
subject_dn | string | |
issuer_dn | string | |
serial_number | string | |
not_before | timestamp | |
not_after | timestamp | |
days_remaining | integer | |
key_type | string | |
key_size | integer | |
fingerprint_sha256 | string | |
certificate_pem | string | |
parent_ca_id | string | |
crl_distribution_url | string | |
ocsp_responder_url | string | |
is_crl_fresh | boolean | |
crl_last_checked | timestamp | |
is_ocsp_responsive | boolean | IsOCSPResponsive means a verified answer was obtained, not that an HTTP request succeeded. Before migration 033 it meant the latter, which any web server at that address could satisfy. |
ocsp_last_checked | timestamp | |
ocsp_status | string | OCSPStatus is what the responder said about this CA certificate: GOOD, REVOKED or UNKNOWN. Empty means never asked, which is not the same as UNKNOWN — that is the responder disclaiming knowledge of a certificate it ought to know about. |
ocsp_revoked_at | timestamp | |
ocsp_last_error | string | OCSPLastError says why the last check produced no verified answer. "The responder is unreachable" and "something answered and it was not the CA" are different problems, and a boolean cannot tell them apart. |
certificates_issued_count | integer | |
alert_thresholds | string | JSON string |
last_alert_sent_at | timestamp | |
last_alert_threshold | integer | |
status | string | HEALTHY, WARNING, CRITICAL, EXPIRED, UNKNOWN |
ca_account_id | string | |
owner_team | string | OwnerTeam and OwnerEmail say who to call. Free text: team names and distribution lists do not live in CertPilot, and a foreign key to something it does not own would mean either an import step or a wrong answer on a row someone is reading at 2am. |
owner_email | string | |
tags | string | |
notes | string | |
source | string | Source says who put this row here: a person, or a gateway that was asked for its issuers. The importer refreshes what a certificate says about itself and must never touch what an operator decided — the name they chose, the thresholds they tuned, the team they put on it — so it needs to know which rows are its own. |
last_seen_at | timestamp | LastSeenAt is when a gateway last reported this CA among its issuers. An old value means the CA has been rotated out of the mount it came from. It is not deleted: it signed certificates that are still being served, and its expiry is still the date those stop working. |
acknowledgement | AlertAcknowledgement | Acknowledgement is the current acknowledgement, when the caller asked for it to be resolved. Not a stored column — see AlertAcknowledgement. |
created_at | timestamp | |
updated_at | timestamp |
CTCertificate
CTCertificate is one certificate a log reported for a watched domain.
| Field | Type | Description |
|---|---|---|
id | string | |
monitor_id | string | |
entry_id | integer | EntryID identifies the log entry, and is what makes a re-check idempotent. |
logged_at | timestamp | |
serial_number | string | |
issuer_dn | string | |
common_name | string | |
sans | string[] | |
not_before | timestamp | |
not_after | timestamp | |
management_state | string | ManagementState is MANAGED or UNMANAGED. Unmanaged here is a stronger signal than on a scan result: a certificate valid for your domain exists, somebody holds its private key, and nothing in this system issued it. |
matched_certificate_id | string | |
is_precertificate | boolean | IsPrecertificate marks the pre-issuance log entry. A precertificate and its final certificate are two entries for one certificate; recorded rather than dropped, but flagged so a count of findings is not doubled. |
first_seen_at | timestamp | |
created_at | timestamp |
CTMonitor
CTMonitor watches Certificate Transparency for one domain. Network scanning answers "what is being served on the addresses I told you about". CT answers a larger question: what has been issued in your name at all — by any CA, to anyone, whether or not it was ever deployed. A developer who obtained a certificate for api.corp.example.com with a personal ACME account appears in no scan of any range, and in CT within minutes.
| Field | Type | Description |
|---|---|---|
id | string | |
domain | string | |
include_subdomains | boolean | IncludeSubdomains watches *.example.com alongside example.com. On by default: the subdomain nobody registered is the one worth finding. |
is_enabled | boolean | |
check_interval_minutes | integer | CheckIntervalMinutes is how often the log is queried. Floored higher than a scan's interval, because the logs are read through a free community service and polling it hard is how everyone loses access to it. |
last_checked_at | timestamp | LastCheckedAt is when a check was last attempted. LastSuccessAt is when one last answered. Keeping these apart is the point of the type. Collapsed into one, a monitor that has been unable to reach the log for a week looks exactly like a monitor that has found nothing for a week — and one of those means nobody is being told about certificates issued in their name. |
last_success_at | timestamp | |
next_check_at | timestamp | |
last_error | string | |
last_entry_id | integer | LastEntryID is the newest log entry already seen, so a later check asks for what is new rather than re-reading years of history. |
certificates_seen | integer | |
unmanaged_seen | integer | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
Certificate
Certificate represents a managed TLS certificate.
| Field | Type | Description |
|---|---|---|
id | string | |
fingerprint_sha256 | string | |
common_name | string | |
sans | string[] | |
serial_number | string | |
issuer_dn | string | |
not_before | timestamp | |
not_after | timestamp | |
days_remaining | integer | |
key_type | string | |
key_size | integer | |
status | string | PENDING, ISSUED, EXPIRING, EXPIRED, REVOKED, RENEWAL_FAILED |
auto_renew | boolean | |
renewal_lead_days | integer | |
last_renewal_attempt | timestamp | |
renewal_error | string | |
renewal_count | integer | |
ca_account_id | string | |
ca_authority_id | string | |
deployment_target_id | string | |
certificate_pem | string | |
chain_pem | string | |
discovered_via | string | MANUAL, SCAN, CT_LOG, IMPORT, REQUESTED |
environment | string | |
team | string | |
tags | string[] | |
metadata | object | Metadata holds values for the admin-defined MetadataFields, keyed by field key. Always non-nil after a read so a template can index it without a guard. |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp | |
key_custody | string | KeyCustody says who holds the private key: CERTPILOT (sealed here), AGENT (on a host, never anywhere else), or EXTERNAL (somebody we cannot name). Until agents existed, "no key stored" meant only the last of those; it now also means the best of the three. |
key_holder_agent_id | string | KeyHolderAgentID is which host, when the answer is AGENT. |
renewal_scheduled_at | timestamp | RenewalScheduledAt is when this certificate should next be renewed, whoever decided it. Nil means nobody has been told anything and the lead time applies. When ARI supplied it, this is a random instant inside the CA's suggested window rather than its start. The randomness is the point of the window: if every client renewed at the start, ARI would move the thundering herd rather than disperse it. |
ari_window_start | timestamp | The CA's renewal advice, as last read. ARIWindowStart and ARIWindowEnd bound the window; ARIExplanationURL is the CA's link to a reason, set when a window has been brought forward — which is exactly when somebody wants to know why. |
ari_window_end | timestamp | |
ari_explanation_url | string | |
ari_checked_at | timestamp | |
ari_next_check_at | timestamp | |
ari_supported | boolean | ARISupported is three-valued on purpose. Nil means nobody has asked yet; false means the CA was asked and does not publish renewal information. Collapsing those would make a CA that has never been checked look identical to one that has nothing to say. |
verification_state | string | VerificationState answers the only question that matters after a renewal: is the thing in front of the users actually serving the new certificate? PENDING, VERIFIED, STALE, UNREACHABLE, NO_ENDPOINTS. A renewal that stored a certificate the server never picked up is the failure this product exists to prevent, produced by this product — the inventory says ninety days remaining and the endpoint says twenty. |
signature_algorithm | string | Cryptographic posture, written by the assessment sweep rather than at issuance, so a certificate that arrives by any of the six routes into this system is assessed the same way. |
public_key_algorithm | string | |
posture_verdict | string | |
posture_summary | string | |
posture_requirements | object | |
quantum_readiness_score | integer | |
quantum_assessed_at | timestamp | |
revoked_at | timestamp | Revocation. RevokedAt and Status must agree — the schema enforces it, because a row carrying a revocation timestamp while still reading ISSUED is the disagreement that makes somebody trust the wrong one. |
revocation_reason | integer | RevocationReason is the RFC 5280 CRLReason, stored as the integer the standard defines because that is what reaches the CRL and the OCSP responder. |
revoked_by | string | |
verify_after | timestamp | VerifyAfter is when the next check may run, set on a successful renewal to now plus a grace period: a deployment done by hand does not happen in the same second as the issuance. |
last_verified_at | timestamp | |
verification_attempts | integer | |
verification_detail | string | VerificationDetail names the endpoints and what they were serving, so the state does not have to be interpreted from a code. |
previous_fingerprint | string | PreviousFingerprint is what this certificate replaced, captured at renewal. It is what makes "still serving the old one" distinguishable from "something else entirely is here" — and the second is a different problem deserving different words. |
CertificateDeployment
CertificateDeployment binds one certificate to one target. The binding, rather than a column on the certificate, because one wildcard on six load balancers is six deployments with six outcomes. A single deployed_at on the certificate would average them into a number that is true of nowhere.
| Field | Type | Description |
|---|---|---|
id | string | |
certificate_id | string | |
target_id | string | |
is_enabled | boolean | |
deploy_on_renewal | boolean | DeployOnRenewal is whether a renewal installs here by itself. A binding created now defaults to true — "install this certificate there" obviously includes "when it changes". Bindings that predate the feature were set to false by migration 023, because an upgrade that silently began writing to production servers would be the fleet-wide mistake this switch exists to bound, delivered by a package manager. |
options | object | Options is per-binding placement — which secret in which namespace, which path. Deliberately not a place for credentials: those belong to the target, which is the thing that holds a connection. |
deployed_fingerprint | string | DeployedFingerprint is what this place was last confirmed to hold. Confirmed by a deploy that returned success, which is a claim about what was sent — not evidence about what is being served. The evidence comes from the verifier, which opens a connection and looks. |
deployed_at | timestamp | |
last_status | string | |
last_error | string | |
target_name | string | TargetName and TargetType are joined in for listings, so a page showing where a certificate goes does not have to issue one lookup per row. |
target_type | string | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
CloudCertificate
CloudCertificate is one certificate found sitting in a cloud store.
| Field | Type | Description |
|---|---|---|
id | string | |
connection_id | string | |
resource_id | string | ResourceID is the provider's own identifier — an ARN, a Key Vault certificate id, a GCP self-link, a namespace/name. Unique per connection, which is what makes a repeated sync an update rather than a duplicate. |
name | string | |
location | string | Location is region, vault, or cluster — wherever the provider says this lives. It is the first thing somebody needs in order to go and look. |
common_name | string | |
subject_dn | string | |
issuer_dn | string | |
serial_number | string | |
sans | string[] | |
not_before | timestamp | |
not_after | timestamp | |
key_type | string | |
key_size | integer | |
fingerprint_sha256 | string | |
certificate_pem | string | |
management_state | string | |
matched_certificate_id | string | |
renewal_mode | string | RenewalMode is what the provider says, verbatim — "IMPORTED", "SELF_MANAGED", "AutoRenew", "cert-manager". Kept as the provider's own word rather than reduced to a boolean, because that word is what somebody has to go and find in their own console. |
will_renew | boolean | WillRenew is whether the provider itself renews this. Nil means the provider did not say, which is not the same as no. |
attached | boolean | Attached reports whether anything is using it. Nil means the provider could not be asked — a Key Vault has no notion of attachment at all — and that is deliberately distinct from false, which means nothing is using it. |
attached_to | string[] | |
findings | Finding[] | |
is_imported | boolean | |
imported_certificate_id | string | |
first_seen_at | timestamp | |
last_seen_at | timestamp | |
removed_at | timestamp | RemovedAt is set when a sync that succeeded no longer found it. The row is kept: a certificate that disappeared from the store is information, and deleting it takes its own history along with it. |
created_at | timestamp |
CloudConnection
CloudConnection is one place certificates are stored that CertPilot did not put them. Credentials never live on this type in the clear: ConfigEncrypted is sealed with the keyring before it reaches the store, and carries json:"-" so that no handler can return it by forgetting to strip it.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
provider | string | |
is_enabled | boolean | |
sync_interval_minutes | integer | |
last_synced_at | timestamp | LastSyncedAt is when a sync was last attempted; LastSuccessAt when one last answered. The same split CTMonitor carries, for the same reason. A connection whose credentials expired three weeks ago must not read like an account that simply has no certificates in it — one of those is an all-clear and the other is a blind spot. |
last_success_at | timestamp | |
next_sync_at | timestamp | |
last_error | string | |
scopes | string[] | Scopes is what the last successful sync actually enumerated, in the provider's own words. A cloud account has several places a certificate can sit, and a tool that covers one of them while presenting itself as covering the provider commits this product's original sin in a new place: a short list that reads as a small estate when it is really a narrow search. |
certificates_seen | integer | |
unmanaged_seen | integer | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
DashboardStats
DashboardStats holds summary statistics for the overview dashboard. The five CA counts partition the estate: every CA lands in exactly one, and they sum to TotalCAs. That matters more than it looks. Before this split, one implementation folded UNKNOWN into CriticalCAs and the other dropped it entirely, so the same database produced different numbers depending on which store was running — and a dashboard whose figures do not add up is one nobody trusts enough to act on.
| Field | Type | Description |
|---|---|---|
total_certificates | integer | |
healthy_certs | integer | |
expiring_soon_certs | integer | |
expired_certs | integer | |
total_cas | integer | |
healthy_cas | integer | |
warning_cas | integer | |
critical_cas | integer | |
expired_cas | integer | |
unknown_cas | integer | UnknownCAs counts authorities that have never been checked, or whose certificate could not be parsed. Shown rather than hidden: a CA nobody can assess is not a healthy one. |
total_scans | integer |
DeploymentJob
DeploymentJob is one installation that has been asked for and not finished. Durable for the same reason a renewal is: a process that dies mid-deploy must leave behind something another process can pick up. Held per binding rather than per certificate — one certificate going to six targets is six jobs, all outstanding at once.
| Field | Type | Description |
|---|---|---|
id | string | |
deployment_id | string | |
certificate_id | string | |
target_id | string | |
reason | string | |
status | string | |
run_after | timestamp | |
attempts | integer | |
locked_by | string | |
locked_until | timestamp | |
last_error | string | |
attempt_log | DeploymentAttempt[] | |
deploy_order | integer | DeployOrder is the wave this job belongs to, copied from the target when the rollout was enqueued. Copied rather than looked up, so that reordering a target cannot change the plan of a rollout already under way — which is how production ends up with a certificate staging never accepted. |
fingerprint | string | Fingerprint is what this job is trying to install, captured at enqueue. Not read off the certificate at run time: a job enqueued by a renewal is for that renewal's certificate, and if a newer one exists there is a newer job behind this one. |
not_after | timestamp | NotAfter is the expiry being raced, so the queue orders by urgency without a join. |
escalated_at | timestamp | |
triggered_by | string | |
actor_email | string | |
started_at | timestamp | |
completed_at | timestamp | |
created_at | timestamp | |
updated_at | timestamp |
DeploymentTarget
DeploymentTarget is one place certificates are installed. Deployment is where CertPilot stops observing and starts changing something that is currently carrying traffic, so a target carries more than an address: whether it is switched on, what went wrong last time, and whether private key material passes through it.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
description | string | |
target_type | string | webhook, kubernetes, aws_acm, azure_kv, gcp_lb, filesystem |
is_enabled | boolean | |
deploys_private_key | boolean | DeploysPrivateKey records whether this target receives key material. Stored rather than derived from the sealed config, so "where does this organisation ship private keys" is answerable by reading the target list — without the KEK, and without decrypting anything. |
deploy_order | integer | DeployOrder is which rollout wave this target belongs to. Lower goes first, and a wave does not start until every earlier one has finished. Zero for everything by default, which is one wave and the behaviour that existed before waves did. A canary is a target on its own in the lowest wave: one target, exactly one attempt, declared rather than inferred. |
cloud_connection_id | string | CloudConnectionID names the account whose credentials this target borrows, for the target types that borrow one. A plain column rather than a field inside the sealed config, and for the same reason DeploysPrivateKey is one: "which cloud accounts can this system write to" has to be answerable by reading the target list, without the KEK and without decrypting anything. |
agent_id | string | AgentID is set when this target is a host running the agent. It changes who does the work rather than what the work is. Everything else in this table is deployed to by a core worker opening a connection; an agent target is deployed to by the host itself claiming the job, because the whole reason that host runs an agent is that nothing can reach inwards to it. The core queue skips these for that reason. |
last_deployment_at | timestamp | Attempted versus succeeded. A target that has been failing all week must not read as one that simply has had nothing to do. |
last_deployment_status | string | |
last_deployment_error | string | |
last_success_at | timestamp | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
DiscoveryResult
DiscoveryResult is what one endpoint was serving when it was asked.
| Field | Type | Description |
|---|---|---|
id | string | |
scan_id | string | |
host | string | |
port | integer | |
reachable | boolean | Reachable is false when no TLS handshake completed. Error then carries the dialler's own words — "connection refused" and "certificate signed by unknown authority" are different problems and only one of them is about certificates. |
error | string | |
management_state | string | |
trust_state | string | |
matched_certificate_id | string | MatchedCertificateID names the managed certificate this fingerprint belongs to, when there is one. |
common_name | string | |
subject_dn | string | |
sans | string[] | |
issuer_dn | string | |
serial_number | string | |
not_before | timestamp | |
not_after | timestamp | |
key_type | string | |
key_size | integer | |
is_ca | boolean | |
fingerprint_sha256 | string | |
certificate_pem | string | |
chain_pem | string | |
chain_length | integer | ChainLength is how many certificates the server sent, leaf included. One means it sent no intermediates, which works only for clients that already happen to hold them. |
tls_version | string | |
cipher_suite | string | |
key_exchange | string | KeyExchange is the negotiated group, e.g. "X25519MLKEM768". Recorded on every scan because it is unrecoverable afterwards and is the basis of any later answer about quantum readiness. |
alpn | string | |
findings | Finding[] | |
is_imported | boolean | |
imported_certificate_id | string | |
scanned_at | timestamp | |
created_at | timestamp |
DiscoveryScan
DiscoveryScan is one run of the scanner over a set of targets.
| Field | Type | Description |
|---|---|---|
id | string | |
scan_type | string | |
targets | string[] | Targets is what was asked for, as given. Kept verbatim so a scan can be repeated and so an unexpected result can be traced back to the input that produced it. |
status | string | |
target_count | integer | TargetCount is how many endpoints the run set out to reach, once the targets expanded — 10.0.0.0/24 is 254 of them. Without it a running scan can say how many endpoints have answered but not out of how many, and a scan whose end nobody can see is one people cancel out of doubt rather than intent. |
results_count | integer | |
unmanaged_count | integer | The three counts partition ResultsCount. UnmanagedCount is the headline: it is the number a PKI team reads first and the only one that implies work. |
managed_count | integer | |
unreachable_count | integer | |
started_at | timestamp | |
completed_at | timestamp | |
error | string | |
triggered_by | string | |
actor_email | string | |
created_at | timestamp |
DiscoverySchedule
DiscoverySchedule is a scan that runs by itself. Discovery run once is a snapshot; run repeatedly it is monitoring. The finding it exists to produce is created continuously — by deployments nobody mentioned and appliances nobody registered — so catching it means looking again without anyone remembering to.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
targets | string[] | Targets as typed, not expanded. A schedule is edited by the person who wrote it, and re-reading 254 addresses is not editing. |
ports | integer[] | |
interval_minutes | integer | IntervalMinutes is how often it runs. Not a cron expression: an interval is what a PKI team wants, and a cron field is a small language whose mistakes are silent — a schedule meant to run nightly that instead runs yearly looks identical on screen to one that works. |
is_enabled | boolean | |
last_run_at | timestamp | LastRunAt is when a run last started, not finished. The next run is computed from it, so a long scan does not push its own schedule later every time it runs. |
next_run_at | timestamp | |
last_scan_id | string | |
last_error | string | LastError is why the last run did not happen. Kept on the schedule so a list can show which one has quietly stopped working: a schedule that fails every night and is never read is worse than no schedule, because it is the appearance of coverage. |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
EndpointTLSPosture
EndpointTLSPosture is what one handshake with one endpoint actually negotiated. The only table in this system whose contents cannot be derived from an inventory. What a certificate is signed with is in the certificate; what an endpoint negotiates is a property of a running server and its configuration, and it takes a real connection to find out.
| Field | Type | Description |
|---|---|---|
id | string | |
host | string | |
port | integer | |
certificate_id | string | |
scan_id | string | |
tls_version | string | |
cipher_suite | string | |
key_exchange_group | string | KeyExchangeGroup is the negotiated group — "X25519MLKEM768", "x25519". |
hybrid_key_exchange | boolean | HybridKeyExchange is whether that group carries a post-quantum key encapsulation, which is the single fact in this record that protects traffic being recorded today. |
offered_hybrid | boolean | OfferedHybrid is whether CertPilot offered one. Without it, HybridKeyExchange being false is a fact about CertPilot rather than about the endpoint. |
supports_tls13 | boolean | |
alpn | string | |
verdict | string | |
summary | string | |
requirements | object | |
observed_at | timestamp |
MeResponse
MeResponse is the caller's own identity and role.
| Field | Type | Description |
|---|---|---|
subject | string | |
email | string | |
display_name | string | |
role | string | |
auth_method | string | AuthMethod distinguishes a person from a wall display from local development, which the UI needs in order to decide what to offer. |
user_id | string | UserID is CertPilot's own identifier, absent for display tokens and anonymous development where no user row exists. |
role_source | string | |
must_change_password | boolean | MustChangePassword is set for a generated credential the account holder has not replaced — the one printed at first start. |
MetadataField
MetadataField is one question every certificate can answer. What a PKI team needs to slice its inventory by belongs to that team — cost centre, change ticket, data classification, which product line — and a fixed schema guesses at it and is wrong everywhere. An admin defines the fields.
| Field | Type | Description |
|---|---|---|
id | string | |
key | string | Key is the immutable machine name certificates store their values under. |
label | string | |
field_type | string | |
options | MetadataOption[] | |
display | string | |
help_text | string | |
is_required | boolean | IsRequired is enforced when a certificate is requested, never retroactively: adding a required field must not invalidate the estate. |
sort_order | integer | |
is_archived | boolean | IsArchived fields stop being offered and keep labelling stored values. |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
NotificationChannel
NotificationChannel is a destination alerts are delivered to.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
channel_type | string | email, slack, webhook |
is_enabled | boolean | |
severity_threshold | string | SeverityThreshold is the minimum severity this channel delivers. |
topics | string[] | Topics restricts which event topics reach this channel. Empty means all of them — a channel that matches nothing looks configured and delivers nothing, which is the failure this whole subsystem exists to avoid. |
last_sent_at | timestamp | |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
Policy
Policy represents a security/compliance policy rule.
| Field | Type | Description |
|---|---|---|
id | string | |
name | string | |
description | string | |
is_enabled | boolean | |
rule_type | string | |
rule_config | string | JSON string |
domain_pattern | string | |
severity | string | INFO, WARNING, BLOCK |
created_by | string | |
created_at | timestamp | |
updated_at | timestamp |
RenewalJob
RenewalJob is one renewal that has been asked for and not yet finished. Renewal is the only part of this system that changes the world; everything else observes. So it exists as a row rather than as a call: a process that dies mid-renewal leaves behind something another process can pick up, rather than a certificate whose fate nobody recorded.
| Field | Type | Description |
|---|---|---|
id | string | |
certificate_id | string | |
reason | string | |
status | string | |
run_after | timestamp | RunAfter is when this job may next be attempted. Retries move it forward; nothing else does. |
attempts | integer | |
locked_by | string | LockedBy and LockedUntil are the lease. A worker that is killed does not need reaping — its claim expires and another worker takes the job. |
locked_until | timestamp | |
last_error | string | |
attempt_log | RenewalAttempt[] | |
not_after | timestamp | NotAfter is the deadline this job is racing, copied from the certificate at enqueue. Denormalised so the queue can be ordered by urgency on every claim without a join. |
ca_account_id | string | CAAccountID is which account's rate limit this renewal spends, denormalised at enqueue for the same reason NotAfter is: it is read on every pacing decision. |
fingerprint_at_enqueue | string | FingerprintAtEnqueue is what the certificate was when the job was created. The crash guard: if the certificate has moved on its own, the renewal already happened and a retry must not issue a second one. |
escalated_at | timestamp | EscalatedAt marks a job a person should look at, so alerting does not have to re-derive that from attempt counts. |
triggered_by | string | |
actor_email | string | |
started_at | timestamp | |
completed_at | timestamp | |
created_at | timestamp | |
updated_at | timestamp |
User
User is CertPilot's own record of a person who has signed in. The identity provider remains the authority on who somebody is; this is the authority on what they may do here. Roles used to come from a claim on the token, which meant that promoting a colleague required an administrator of the identity provider and took effect only when that person's token next refreshed — the wrong ownership for a team that runs the CA hierarchy but rarely runs Okta.
| Field | Type | Description |
|---|---|---|
id | string | |
issuer | string | Issuer and Subject together are the identity. A subject is unique only within the issuer that minted it, so neither half means anything alone. |
subject | string | |
email | string | |
display_name | string | |
role | string | |
status | string | |
role_source | string | RoleSource distinguishes a deliberate grant from a default and from a bootstrap, so a users list can be read without guessing. |
must_change_password | boolean | MustChangePassword marks a credential the holder did not choose — the generated one printed at first start. Not a control in itself; it is what lets the UI insist rather than hope. |
last_seen_at | timestamp | |
created_at | timestamp | |
updated_at | timestamp |