Skip to content

AbstractCore v2.20.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 02:30
· 372 commits to main since this release

Email becomes a configured, encrypted, policy-bound account. The terminal console ships as
abstractcore-console 0.5.0 with a new Email screen.

Added

  • abstractcore.comms.email: one typed mail library used by the CLI, the consoles, the tools and
    hosts. IMAP is read-only (EXAMINE, BODY.PEEK, and a connection that refuses any mutating
    command); SMTP sends with attachments and reply threading; TLS is always verified (SSL or
    STARTTLS, optional private CA); search takes typed fields (sender address or domain, recipient,
    subject text, dates, read state); fetch_new reads incrementally with UIDVALIDITY cursors;
    bodies are returned whole. Errors are typed from protocol codes with a cause and a fix
    (email_auth_failed, email_tls_failed, email_unreachable, email_mailbox_missing,
    email_quota_exceeded, email_recipient_refused, ...).
  • Credentials encrypted at rest (AES-256-GCM, key in the OS keychain; a 0600 key file on hosts
    without one, reported by status).
  • OAuth2 sign-in for Google and Microsoft (XOAUTH2 on IMAP and SMTP, token refresh and rotation,
    device-code and browser flows, custom providers) with your own OAuth client or the built-in
    AbstractFramework client when one is registered for the provider (BUILTIN_CLIENTS,
    resolve_oauth_client; settings record client_source). Available from the CLI (--oauth google|microsoft --client-id <value> --client-secret <value>; with --json the sign-in prompt
    is one stderr line {"oauth_prompt": ...}), /acore/email/oauth/start|finish|cancel, the web
    console (Sign in with OAuth2) and the terminal console (g).
  • Recipient policy (allowlist / denylist of exact addresses and domains over To, Cc and Bcc;
    any refused recipient refuses the whole message with the addresses and rule named). A new account
    starts with an allowlist holding the registered address.
  • Send limits per rolling hour and day (default 20 / 100), durable across processes.
  • abstractcore email connect|test|status|folders|disconnect|enable|disable|registered-address,
    abstractcore email policy show|set|check, abstractcore email limits show|set (all with
    --json); credentials as direct parameters.
  • /acore/email server routes and an Email tab in the web console; an Email screen (@)
    in the terminal console.
  • Tools search_emails, get_email_attachment and reply_email; email tool results carry
    content_trust: "untrusted" and a fixed notice. Hosts inject a per-user account with
    set_email_account_resolver / use_email_context.
  • Refiner id send_email_recipient@v2 (self set plus pre-authorised recipients), declared on
    send_email and reply_email.
  • abstractcore.testing.mailserver: hermetic IMAP, SMTP (aiosmtpd) and OAuth2 servers with a
    throwaway CA for tests. aiosmtpd joins the test extra.
  • Base dependencies cryptography and keyring.
  • Agent email tools (off by default): the email tools use the local account only when it is
    turned on (abstractcore email agent-tools on|off, PUT /acore/email/agent-tools, the web
    console's Email tab, a on the terminal console's Email screen); otherwise they answer
    email_agent_tools_off. The settings document carries agent_tools: {enabled, active, reason}
    (config field email.agent_tools). Hosts with their own resolver keep their own switch.
  • Reading by MIME structure: read_email / EmailClient.get fetch BODYSTRUCTURE and then only
    the text and HTML parts; get_email_attachment / download_attachment fetch only that part.
    A reading limit (max_message_bytes, default 25 MiB) returns a typed skip record
    (email_message_too_large, body_skipped, bodies null) instead of the bodies.
  • Message summaries add has_attachments, reply_to, in_reply_to, list_unsubscribe and the
    typed priority headers importance, x_priority, priority; search_emails(has_attachment=)
    and SearchCriteria(has_attachment=); a list_email_folders tool.
  • list_emails / search_emails return has_more and next_cursor (pass cursor= for the next
    page); limit is 1 to 100 (MAX_LIST_LIMIT).
  • Both consoles name the account they configure (the AbstractCore install's, with its settings
    file); Email explains how it differs from a gateway user's account.

Changed

  • send_email no longer accepts a headers argument (From and Reply-To are always the account's;
    threading headers come from reply_email). read_email's max_body_chars is accepted and
    ignored: bodies are never truncated.
  • send_email results no longer include the SMTP host and user name.
  • Attachment size in read_email / MessageDetail is the part's size on the wire (encoded) as
    the server reports it; the saved file's size is in the download result.
  • fetch_new after a UIDVALIDITY rebuild with nothing to resynchronise returns a new baseline
    (reset=True, baseline=True, cursor at the newest message) instead of a cursor at UID 0.
  • A limit above 100 in list_emails / search_emails is refused with email_invalid_settings.

Removed

  • abstractcore.tools.email_digests.send_email_digest (sending outside the tools and the host's
    approval gate). Render with render_email_digest_text and send with send_email or
    EmailContext.send.
  • The terminal console's Server screen no longer lists the legacy flat email fields.

Migration

  • The ABSTRACT_EMAIL_* environment variables, the ABSTRACT_EMAIL_ACCOUNTS_CONFIG accounts file
    and the flat email.smtp_* / email.imap_* config fields are no longer read at use time. The
    first time email is used without a configured account, that configuration is imported once
    (password included, then sealed); afterwards the variables are ignored and abstractcore email status names each one still set and its replacement. Only the default account of a
    multi-account file is imported. configs/emails.yaml is removed.
  • Hosts that grant tools by risk refiner: send_email now declares send_email_recipient@v2; an
    enforcement lane that only knows @v1 holds the approval ceiling (asks) until it implements v2.