Skip to main content

Account

Struct Account 

Source
pub struct Account {
Show 13 fields pub id: String, pub profile: String, pub pubkey: Vec<u8>, pub contact: Vec<String>, pub status: String, pub created_at: i64, pub eab_kid: Option<String>, pub terms_of_service_agreed: Option<bool>, pub created_ip: Option<String>, pub created_ptr: Option<String>, pub last_seen_at: Option<i64>, pub last_seen_ip: Option<String>, pub last_seen_ptr: Option<String>,
}
Expand description

An ACME account (RFC 8555 §7.1.2), keyed by the client’s public key stored as DER SPKI. contact is persisted as a JSON array of strings.

§ACME Protocol Compliance

This struct represents the account object as defined in RFC 8555:

  • id: Unique identifier for the account (UUID)
  • pubkey: DER-encoded SPKI public key used for authentication
  • contact: Array of contact URIs (email, etc.) for the account holder
  • status: Account status (valid, deactivated, etc.)
  • created_at: Timestamp when the account was created

§Storage Details

  • The public key is stored in DER SPKI format for consistent hashing and lookup
  • Contact information is serialized as JSON for flexible storage
  • The ID is generated as a UUID v4 for uniqueness
  • Status is tracked to support account lifecycle management

§Methods

  • find_by_pubkey: Lookup account by public key
  • find_by_id: Lookup account by ID
  • find_or_create: Create new account or return existing one (RFC 8555 §7.3)
  • list_all: List every account, oldest first (admin CLI)
  • delete: Hard-delete an account, cascading to its orders (admin CLI)
  • to_json: Convert to RFC 8555 account JSON object format

Fields§

§id: String§profile: String

The ACME endpoint ([profiles.<name>]) this account was registered at. Accounts are keyed by (profile, pubkey), so the same client key at two endpoints is two accounts — see the schema comment in migrations/20260722210000_add_accounts.sql for why that is a security property and not just tidiness.

§pubkey: Vec<u8>§contact: Vec<String>§status: String§created_at: i64§eab_kid: Option<String>

Which EAB credential (if any) created this account – an audit trail only, set once and never overwritten. See Account::set_eab_kid.

§terms_of_service_agreed: Option<bool>

Whether this account agreed to the terms of service when it was created (RFC 8555 §7.3.3). None for an account created at an endpoint that advertised none — which is not the same as “declined”, and renders as an absent member rather than false. Set once, at creation; see Account::set_terms_agreed.

§created_ip: Option<String>

Where newAccount was called from, and the reverse name that address had at the time. Traceability only — see the schema comment in migrations/20260722210000_add_accounts.sql for why nothing ever compares against these.

§created_ptr: Option<String>§last_seen_at: Option<i64>

When this key last authenticated a request, and from where. Advanced by Account::touch under the ACCOUNT_TOUCH_INTERVAL throttle.

§last_seen_ip: Option<String>§last_seen_ptr: Option<String>

Implementations§

Source§

impl Account

Source

pub async fn find_by_pubkey( profile: &str, pubkey: &[u8], database: &Database, ) -> Result<Option<Account>, Error>

Source

pub async fn find_by_id( profile: &str, id: &str, database: &Database, ) -> Result<Option<Account>, Error>

Looks an account up by id within one profile. An id is a UUID and therefore globally unique, so the profile predicate is not about finding the row: it is what makes an account URL minted at one endpoint unusable as a kid at another.

Source

pub async fn find_or_create( profile: &str, pubkey: &[u8], contact: Vec<String>, client: &ClientContext, database: &Database, ) -> Result<(Account, bool), Error>

Looks up the account for pubkey, creating it if absent. Returns the account and whether it was newly created — RFC 8555 §7.3 find-or-create, where a repeated key returns the existing account rather than a duplicate.

client is stamped onto the row only on the creating branch: the created_* columns mean “where this account was registered from”, so a later newAccount from elsewhere returning the same account must not rewrite them. Where the key was last used from is last_seen_*, which Account::touch keeps up to date.

Source

pub fn needs_touch(&self, now: i64, ip: Option<&str>) -> bool

Whether Account::touch is worth a write at now, for a request arriving from ip.

Two ways to say yes, and the second is the point of the method existing:

  • ACCOUNT_TOUCH_INTERVAL has elapsed (or nothing was ever recorded);
  • the address differs from the last one recorded, whatever the interval says. A key that moves is the single most interesting thing these columns can show, and a throttle that swallowed the move for a minute would hide exactly the requests worth seeing — a stolen account key being used from somewhere new arrives as a burst, not a trickle.

A pure function of the row and its arguments, so the policy is testable without an HTTP request, and it lives beside the columns it governs rather than in the extractor that calls it.

Source

pub async fn touch( &mut self, client: &ClientContext, database: &Database, ) -> Result<(), Error>

Records that this key just authenticated a request, from client.

Called only when Account::needs_touch said so — which is also why the reverse lookup belongs to the caller: resolving a PTR record for a write that is about to be skipped would be the cost the throttle exists to avoid. Keeps the in-memory fields in sync, so a to_json in the same request reflects it without a re-read.

Source

pub async fn update_contact( &mut self, contact: Vec<String>, database: &Database, ) -> Result<(), Error>

Replaces the account’s contact list (RFC 8555 §7.3.2 account update). The in-memory self.contact is kept in sync so a subsequent to_json reflects the change without a re-read.

Source

pub async fn deactivate(&mut self, database: &Database) -> Result<(), Error>

Deactivates the account (RFC 8555 §7.3.6): sets status to deactivated, a terminal state. Keeps self.status in sync.

Source

pub async fn update_pubkey( &mut self, pubkey: &[u8], database: &Database, ) -> Result<(), Error>

Replaces the account’s key (RFC 8555 §7.3.5 account key rollover). pubkey is DER SPKI, the same form every other lookup keys accounts by. pubkey is UNIQUE, a backstop against the rare race the caller’s own pre-check (Account::find_by_pubkey) cannot fully close; a violation here surfaces as a plain sqlx::Error.

Source

pub async fn set_eab_kid( &mut self, eab_kid: &str, database: &Database, ) -> Result<(), Error>

Records which EAB credential created this account – an audit trail only (see the migration comment). Called once, right after Account::find_or_create reports a freshly created row (post_new_account in lib.rs): never overwritten afterwards, so re-registering under an existing key does not change what is recorded, even if a different (still valid) EAB credential is presented that time.

Source

pub async fn set_terms_agreed( &mut self, database: &Database, ) -> Result<(), Error>

Records that this account agreed to the terms of service (RFC 8555 §7.3.3).

Same lifecycle as Account::set_eab_kid: called once, right after find_or_create reports a freshly created row, and never overwritten — re-registering under an existing key does not restate the agreement, and a ToS added to the configuration later does not retroactively make old accounts look like they accepted it.

Source

pub async fn find_any_by_id( id: &str, database: &Database, ) -> Result<Option<Account>, Error>

Looks an account up by id across every profile — for the admin CLI, where an operator holds an id and not necessarily the endpoint it came from. Ids are UUIDs, so this is unambiguous.

Never use it on a request path: profile scoping is what keeps an account URL minted at one endpoint from being accepted at another.

Source

pub async fn list_all( profile: Option<&str>, database: &Database, ) -> Result<Vec<Account>, Error>

Lists accounts, oldest first — the admin CLI’s listing. profile filters to one endpoint; None lists every account of every profile, which is what an operator asking “what is on this server?” wants.

Source

pub async fn search( profile: Option<&str>, limit: i64, offset: i64, database: &Database, ) -> Result<(Vec<Account>, i64), Error>

One page of accounts, newest first, plus the total the same filter matches unpaged.

The Account counterpart to crate::sqlite::order::Order::search, and additive for the same reason: Account::list_all stays as it is for the admin CLI, which wants everything.

Two literal statements per branch rather than a builder: with one optional filter there are only two shapes, and sqlx::query’s &'static str bound is a guarantee worth keeping where it is free.

Source

pub async fn delete(id: &str, database: &Database) -> Result<bool, Error>

Hard-deletes the account row — cascading, via ON DELETE CASCADE, to its orders, authorizations and challenges. Returns whether a row existed to delete, so the caller can distinguish “gone” from “never there”.

Source

pub fn to_json(&self, base_url: &str) -> Value

The RFC 8555 account object: status, optional contact, and the orders list URL (derived from the public base_url).

Trait Implementations§

Source§

impl Debug for Account

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more