Introduction
This is the documentation for Inbucket. It is a work in progress; please report mistakes or missing information via GitHub issues.
Intended Usage
Inbucket can be used in many different scenarios. It was designed to be used during software development and testing, and is not intended for storing mail of any significance.
Local Development
graph TD; subgraph Workstation app1["App 1"]-->Inbucket app2["App 2"]-->Inbucket end
Inbucket can be run without installing, after extracting the release for your
platform, start in a terminal inside the directory containing the extracted
files and run ./inbucket
or inbucket.exe
. This will launch an server
listening for SMTP messages on port 2500, and the web interface will be served
on port 9000.
Docker Compose
Companion Mail Server
Installation
Version Selection
In general, Inbucket pre-release (beta and release-candidate) versions are very stable. If you can accept some risk, it is recommended to install a recent pre-release over a very old stable release.
Release notes can be found on GitHub: https://github.com/inbucket/inbucket/releases
The Inbucket news page often summarizes major changes and risks for a particular release.
Docker Build
Inbucket images are automatically built and pushed to Docker Hub and the GitHub Container Registry. The main Inbucket repo contains the Dockerfile, which allows you to build your own Docker image.
Running
docker run -d --name inbucket -p 9000:9000 -p 2500:2500 -p 1100:1100 inbucket/inbucket
Then point your browser at http://localhost:9000/.
Image Tags
latest
tracks the most recent stable releaseedge
tracks the potentially unstablemain
branch- versions are also tagged, ie
3.1.0-beta2
Volumes
/config
will contain the defaultgreeting.html
file (after first start,) which may be customized for your users/storage
holds emails received by Inbucket
Default Configuration
The initial configuration is set by the ENV
statements in the Configuration
section of our Dockerfile. You may use Docker environment variables to
override or further customize Inbucket.
Linux Package Installation
Obtain Package File
Inbucket releases are published on GitHub: https://github.com/inbucket/inbucket/releases
For a Debian or Ubuntu based distribution, download the appropriate .deb
file
for your CPU architecture.
If you are running a RedHat or Fedora based distribution, download the
appropriate .rpm
file for your CPU architecture.
Install Package
The commands below should be run as the root
user.
Debian
dpkg -i inbucket_VER_linux_amd64.deb
RedHat
rpm -ivh inbucket_VER_linux_amd64.rpm
Enable & Start Inbucket
As root
, start the daemon and check for errors:
# Set the daemon to start at boot
systemctl enable inbucket
# Manually start the daemon
systemctl start inbucket
# Confirm Inbucket stayed running
systemctl status inbucket
# Check system logs for startup errors
journalctl -u inbucket
Access Web Interface
Confirm that Inbucket is listening on the default port: http://localhost:9000/.
Customize Inbucket Configuration
Inbucket is configured via environment variables, please see config.md for
more information; or try our Configurator web app. You'll need to modify the
Environment=
statements in
/etc/systemd/system/multi-user.target.wants/inbucket.service
for the
configuration changes to become permanent.
If something goes wrong, you may refer to the default systemd unit on GitHub.
Inbucket Configuration
Inbucket is configured via environment variables. Most options have a reasonable default, but it is likely you will need to change some to suite your desired use cases.
Running inbucket -help
will yield a condensed summary of the environment
variables it supports:
KEY DEFAULT DESCRIPTION
INBUCKET_LOGLEVEL info debug, info, warn, or error
INBUCKET_LUA_PATH inbucket.lua Lua script path
INBUCKET_MAILBOXNAMING local Use local, full, or domain addressing
INBUCKET_SMTP_ADDR 0.0.0.0:2500 SMTP server IP4 host:port
INBUCKET_SMTP_DOMAIN inbucket HELO domain
INBUCKET_SMTP_MAXRECIPIENTS 200 Maximum RCPT TO per message
INBUCKET_SMTP_MAXMESSAGEBYTES 10240000 Maximum message size
INBUCKET_SMTP_DEFAULTACCEPT true Accept all mail by default?
INBUCKET_SMTP_ACCEPTDOMAINS Domains to accept mail for
INBUCKET_SMTP_REJECTDOMAINS Domains to reject mail for
INBUCKET_SMTP_REJECTORIGINDOMAINS Domains to reject mail from
INBUCKET_SMTP_DEFAULTSTORE true Store all mail by default?
INBUCKET_SMTP_STOREDOMAINS Domains to store mail for
INBUCKET_SMTP_DISCARDDOMAINS Domains to discard mail for
INBUCKET_SMTP_TIMEOUT 300s Idle network timeout
INBUCKET_SMTP_TLSENABLED false Enable STARTTLS option
INBUCKET_SMTP_TLSPRIVKEY cert.key X509 Private Key file for TLS Support
INBUCKET_SMTP_TLSCERT cert.crt X509 Public Certificate file for TLS Support
INBUCKET_POP3_ADDR 0.0.0.0:1100 POP3 server IP4 host:port
INBUCKET_POP3_DOMAIN inbucket HELLO domain
INBUCKET_POP3_TIMEOUT 600s Idle network timeout
INBUCKET_WEB_ADDR 0.0.0.0:9000 Web server IP4 host:port
INBUCKET_WEB_BASEPATH Base path prefix for UI and API URLs
INBUCKET_WEB_UIDIR ui/dist User interface dir
INBUCKET_WEB_GREETINGFILE ui/greeting.html Home page greeting HTML
INBUCKET_WEB_MONITORVISIBLE true Show monitor tab in UI?
INBUCKET_WEB_MONITORHISTORY 30 Monitor remembered messages
INBUCKET_WEB_PPROF false Expose profiling tools on /debug/pprof
INBUCKET_STORAGE_TYPE memory Storage impl: file or memory
INBUCKET_STORAGE_PARAMS Storage impl parameters, see docs.
INBUCKET_STORAGE_RETENTIONPERIOD 24h Duration to retain messages
INBUCKET_STORAGE_RETENTIONSLEEP 50ms Duration to sleep between mailboxes
INBUCKET_STORAGE_MAILBOXMSGCAP 500 Maximum messages per mailbox
The following sections will describe each of these in more detail.
Global Configuration
These options affect multiple parts of Inbucket.
Log Level
- Name:
INBUCKET_LOGLEVEL
- Default:
info
- Values: one of
debug
,info
,warn
, orerror
This setting controls the verbosity of log output. A small desktop installation
should probably select info
, but a busy shared installation would be better
off with warn
or error
.
Lua Script
- Name:
INBUCKET_LUA_PATH
- Default:
inbucket.lua
- Values: relative or fully qualified path to a file
This is the path to the (optional) Inbucket Lua script. If the specified file is present, Inbucket will load it during startup. Ignored if the file is not found, or the setting is empty.
Mailbox Naming
- Name:
INBUCKET_MAILBOXNAMING
- Default:
local
- Values: one of
local
orfull
ordomain
The mailbox naming setting determines the name of a mailbox for an incoming message, and thus where it must be retrieved from later.
Prior to the addition of the mailbox naming setting, Inbucket always operated in
local
mode. Regardless of this setting, the +
wildcard/extension is not
incorporated into the mailbox name.
Naming Modes
local
ensures the domain is removed, such that:
james@inbucket.org
is stored injames
james+spam@inbucket.org
is stored injames
full
retains the domain as part of the name, such that:
james@inbucket.org
is stored injames@inbucket.org
james+spam@inbucket.org
is stored injames@inbucket.org
domain
ensures the local-part is removed, such that:
james@inbucket.org
is stored ininbucket.org
matt@inbucket.org
is stored ininbucket.org
matt@notinbucket.com
is stored innotinbucket.com
SMTP Service Configuration
Address and Port
- Name:
INBUCKET_SMTP_ADDR
- Default:
0.0.0.0:2500
The IPv4 address and TCP port number the SMTP server should listen on, separated by a colon. Some operating systems may prevent Inbucket from listening on port 25 without escalated privileges. Using an IP address of 0.0.0.0 will cause Inbucket to listen on all available network interfaces.
Greeting Domain
- Name:
INBUCKET_SMTP_DOMAIN
- Default:
inbucket
The domain used in the SMTP greeting:
220 domain Inbucket SMTP ready
Most SMTP clients appear to ignore this value.
Maximum Recipients
- Name:
INBUCKET_SMTP_MAXRECIPIENTS
- Default:
200
Maximum number of recipients allowed (SMTP RCPT TO
phase). If you are testing
a mailing list server, you may need to increase this value. For comparison, the
Postfix SMTP server uses a default of 1000, it would be unwise to exceed this.
Maximum Message Size
- Name:
INBUCKET_SMTP_MAXMESSAGEBYTES
- Default:
10240000
(10MB)
Maximum allowable size of a message (including headers) in bytes. Messages
exceeding this size will be rejected during the SMTP DATA
phase.
Default Recipient Accept Policy
- Name:
INBUCKET_SMTP_DEFAULTACCEPT
- Default:
true
- Values:
true
orfalse
If true, Inbucket will accept mail to any domain unless present in the reject domains list. If false, recipients will be rejected unless their domain is present in the accept domains list.
Accepted Recipient Domain List
- Name:
INBUCKET_SMTP_ACCEPTDOMAINS
- Default: None
- Values: Comma separated list of recipient domains
- Example:
localhost,mysite.org
List of domains to accept mail for when INBUCKET_SMTP_DEFAULTACCEPT
is false;
has no effect when true.
Rejected Recipient Domain List
- Name:
INBUCKET_SMTP_REJECTDOMAINS
- Default: None
- Values: Comma separated list of recipient domains
- Example:
reject.com,gmail.com
List of domains to reject mail for when INBUCKET_SMTP_DEFAULTACCEPT
is true;
has no effect when false.
Rejected Origin Domain List
- Name:
INBUCKET_SMTP_REJECTORIGINDOMAINS
- Default: None
- Values: Comma separated list of origin domains
- Example:
reject.com,gmail.com
List of domains to reject mail from. This list is enforced regardless of the
INBUCKET_SMTP_DEFAULTACCEPT
value.
Enforcement takes place during evalation of the MAIL FROM
SMTP command, the
origin domain is extracted from the address presented and compared against the
list. It does not take email headers into account.
Default Recipient Store Policy
- Name:
INBUCKET_SMTP_DEFAULTSTORE
- Default:
true
- Values:
true
orfalse
If true, Inbucket will store mail sent to any domain unless present in the discard domains list. If false, messages will be discarded unless their domain is present in the store domains list.
Stored Recipient Domain List
- Name:
INBUCKET_SMTP_STOREDOMAINS
- Default: None
- Values: Comma separated list of recipient domains
- Example:
localhost,mysite.org
List of domains to store mail for when INBUCKET_SMTP_DEFAULTSTORE
is false;
has no effect when true.
Discarded Recipient Domain List
- Name:
INBUCKET_SMTP_DISCARDDOMAINS
- Default: None
- Values: Comma separated list of recipient domains
- Example:
recycle.com,loadtest.org
Mail sent to these domains will not be stored by Inbucket. This is helpful if
you are load or soak testing a service, and do not plan to inspect the resulting
emails. Messages sent to a domain other than this will be stored normally.
Only has an effect when INBUCKET_SMTP_DEFAULTSTORE
is true.
Network Idle Timeout
- Name:
INBUCKET_SMTP_TIMEOUT
- Default:
300s
- Values: Duration ending in
s
for seconds,m
for minutes
Delay before closing an idle SMTP connection. The SMTP RFC recommends 300 seconds. Consider reducing this significantly if you plan to expose Inbucket to the public internet.
TLS Support Availability
- Name:
INBUCKET_SMTP_TLSENABLED
- Default:
false
- Values:
true
orfalse
Enable the STARTTLS option for opportunistic TLS support
TLS Private Key File
- Name:
INBUCKET_SMTP_TLSPRIVKEY
- Default:
cert.key
- Values: filename or path to private key
- Example:
server.privkey
Specify the x509 Private key file to be used for TLS negotiation. This option is only valid when INBUCKET_SMTP_TLSENABLED is enabled.
TLS Public Certificate File
- Name:
INBUCKET_SMTP_TLSCERT
- Default:
cert.crt
- Values: filename or path to the certificate key
- Example:
server.crt
Specify the x509 Certificate file to be used for TLS negotiation. This option is only valid when INBUCKET_SMTP_TLSENABLED is enabled.
POP3 Service Configuration
Address and Port
- Name:
INBUCKET_POP3_ADDR
- Default:
0.0.0.0:1100
The IPv4 address and TCP port number the POP3 server should listen on, separated by a colon. Some operating systems may prevent Inbucket from listening on port 110 without escalated privileges. Using an IP address of 0.0.0.0 will cause Inbucket to listen on all available network interfaces.
Greeting Domain
- Name:
INBUCKET_POP3_DOMAIN
- Default:
inbucket
The domain used in the POP3 greeting:
+OK Inbucket POP3 server ready <26641.1522000423@domain>
Most POP3 clients appear to ignore this value.
Network Idle Timeout
- Name:
INBUCKET_POP3_TIMEOUT
- Default:
600s
- Values: Duration ending in
s
for seconds,m
for minutes
Delay before closing an idle POP3 connection. The POP3 RFC recommends 600 seconds. Consider reducing this significantly if you plan to expose Inbucket to the public internet.
Web Interface Configuration
Address and Port
- Name:
INBUCKET_WEB_ADDR
- Default:
0.0.0.0:9000
The IPv4 address and TCP port number the HTTP server should listen on, separated by a colon. Some operating systems may prevent Inbucket from listening on port 80 without escalated privileges. Using an IP address of 0.0.0.0 will cause Inbucket to listen on all available network interfaces.
Base Path
- Name:
INBUCKET_WEB_BASEPATH
- Default: None
Base path prefix for UI and API URLs. This option is used when you wish to root all Inbucket URLs to a specific path when placing it behind a reverse-proxy.
For example, setting the base path to prefix
will move:
- the Inbucket status page from
/status
to/prefix/status
, - Bob's mailbox from
/m/bob
to/prefix/m/bob
, and - the REST API from
/api/v1/*
to/prefix/api/v1/*
.
This setting will not work correctly when accessing Inbucket via the parcel development server.
UI Directory
- Name:
INBUCKET_WEB_UIDIR
- Default:
ui/dist
- Values: Operating system specific path syntax
This directory contains the templates and static assets for the web user
interface. You will need to change this if the current working directory
doesn't contain the ui
directory at startup.
Inbucket will load templates from the templates
sub-directory, and serve
static assets from the static
sub-directory.
Greeting HTML File
- Name:
INBUCKET_WEB_GREETINGFILE
- Default:
ui/greeting.html
The content of the greeting file will be injected into the front page of Inbucket. It can be used to instruct users on how to send mail into your Inbucket installation, as well as link to REST documentation, etc.
Monitor Visible
- Name:
INBUCKET_WEB_MONITORVISIBLE
- Default:
true
- Values:
true
orfalse
If true, the Monitor tab will be available, allowing users to observe all messages received by Inbucket as they arrive. Disabling the monitor facilitates security through obscurity.
This setting has no impact on the availability of the underlying WebSocket, which may be used by other parts of the Inbucket interface or continuous integration tests.
Monitor History
- Name:
INBUCKET_WEB_MONITORHISTORY
- Default:
30
- Values: Integer greater than or equal to 0
The number of messages to remember on the server for new Monitor clients. Does not impact the amount of new messages displayed by the Monitor. Increasing this has no appreciable impact on memory use, but may slow down the Monitor user interface.
This setting has the same effect on the amount of messages available via WebSocket.
Setting to 0 will disable the monitor, but will probably break new mail notifications in the web interface when I finally get around to implementing them.
Performance Profiling & Debug Tools
- Name:
INBUCKET_WEB_PPROF
- Default:
false
- Values:
true
orfalse
When true
, Go's pprof package will be installed to the /debug/pprof
URI.
This exposes detailed memory and CPU performance data for debugging Inbucket
internals.
If this option is enabled, please make sure the URI is not exposed to the public internet, as its use can significantly impact performance.
For more information and example usage, please see the pprof pkg docs.
Mail Storage Configuration
Storage Type
- Name:
INBUCKET_STORAGE_TYPE
- Default:
memory
- Values:
file
ormemory
Selects the storage implementation to use. Currently Inbucket supports two:
file
: stores messages as individual files in a nested directory structure based on the hash of the mailbox name. Each mailbox also includes an index file to speed up enumeration of the mailbox contents.memory
: stores messages in RAM, they will be lost if Inbucket is restarted, or crashes, etc.
File storage is recommended for larger/shared installations. Memory is better suited to desktop or continuous integration test use cases.
Storage Parameters
- Name:
INBUCKET_STORAGE_PARAMS
- Default: None
- Examples:
maxkb:10240
orpath:/tmp/inbucket
Parameters specific to the storage type selected. Formatted as a comma separated list of key:value pairs.
file
type parameters
path
: Operating system specific path to the directory where mail should be stored.$
characters will be replaced with:
in the final path value, allowing Windows drive letters, i.e.D$\inbucket
.
memory
type parameters
maxkb
: Maximum size of the mail store in kilobytes. The oldest messages in the store will be deleted to enforce the limit. In-memory storage has some overhead, for now it is recommended to set this to half the total amount of memory you are willing to allocate to Inbucket.
Retention Period
- Name:
INBUCKET_STORAGE_RETENTIONPERIOD
- Default:
24h
- Values: Duration ending in
m
for minutes,h
for hours, or0
to disable
If set, Inbucket will scan the contents of its mail store once per minute, removing messages older than this. This will be enforced regardless of the type of storage configured. In most cases, the configured value should be significantly longer than one minute.
Retention Sleep
- Name:
INBUCKET_STORAGE_RETENTIONSLEEP
- Default:
50ms
- Values: Duration ending in
ms
for milliseconds,s
for seconds
Duration to sleep between scanning each mailbox for expired messages. Increasing this number will reduce disk thrashing, but extend the length of time required to complete a scan of the entire mail store.
This delay is still enforced for memory
stores, but could be reduced from the
default. Setting to 0
may degrade performance of HTTP/SMTP/POP3 services.
Per Mailbox Message Cap
- Name:
INBUCKET_STORAGE_MAILBOXMSGCAP
- Default:
500
- Values: Positive integer, or
0
to disable
Maximum messages allowed in a single mailbox, exceeding this will cause older messages to be deleted from the mailbox.
Extending Inbucket
Inbucket features a REST API that may be used to read and erase the contents of mailboxes. Outgoing webhook functionality is not included, but can be implemented with Lua.
Lua scripting is the primary way to extend Inbucket's functionality. Event handling functions may be used to react to incoming messages, or alter the way Inbucket processes them.
It is also possible to extend Inbucket in a similar fashion in Go, but due to the lack of a plugin loader, it will requiring building Inbucket from source.
The following chapters cover each of these extension mechanisms.
REST API
Allows clients to retrieve, and delete the contents of mailboxes and messages.
Clients
- Go: https://godoc.org/github.com/inbucket/inbucket/pkg/rest/client
- Java: https://github.com/stepstone-tech/inbucket-java-client
- Node.js: https://github.com/Xotabu4/inbucket-js-client
- Shell: https://github.com/inbucket/inbucket/blob/master/etc/rest-apiv1.sh
API v1
- List Mailbox Contents: GET /api/v1/mailbox/{name}
- Get Message: GET /api/v1/mailbox/{name}/{id}
- Get Message Source: GET /api/v1/mailbox/{name}/{id}/source
- Delete Message: DELETE /api/v1/mailbox/{name}/{id}
- Purge Mailbox Contents: DELETE /api/v1/mailbox/{name}
List Mailbox Contents
Retrive the list of messages and their metadata for the specified mailbox.
Specification
URI
GET /api/v1/mailbox/{name}
Parameters
name
- name of the mailbox to list the contents of
Output
A JSON array of message header maps, containing the following fields:
mailbox
- name of the mailbox the message belongs toid
- message identifier, will be unique for this mailbox, but not across mailboxesfrom
- message sendersubject
- message subject linedate
- date message was received (not the date specified by SMTP headers) in ISO 8601 format.size
- size of headers + message in bytes
Example
Request: curl -i -H "Accept: application/json" http://localhost:9000/api/v1/mailbox/swaks
Response: (JSON reformatted for readability)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Expires: -1
Content-Length: 686
Date: Tue, 15 Oct 2013 23:54:36 GMT
[
{
"mailbox":"swaks",
"id":"20131015T161202-0000",
"from":"jamehi03@server.com",
"subject":"Swaks Plain Text",
"date":"2013-10-15T16:12:02.231532239-07:00",
"size":264
},
{
"mailbox":"swaks",
"id":"20131015T161202-0001",
"from":"jamehi03@server.com",
"subject":"Swaks HTML",
"date":"2013-10-15T16:12:02.292145012-07:00",
"size":705
},
{
"mailbox":"swaks",
"id":"20131015T161202-0002",
"from":"jamehi03@server.com",
"subject":"Swaks Attachment",
"date":"2013-10-15T16:12:02.358027334-07:00",
"size":4802
},
{
"mailbox":"swaks",
"id":"20131015T161202-0003",
"from":"jamehi03@server.com",
"subject":"Test of ȇɲʢȯȡɪɴʛ",
"date":"2013-10-15T16:12:02.424206527-07:00",
"size":2946
}
]
Get Message
Retrieve a specific message, with the headers and MIME data parsed into JSON.
Specification
URI
GET /api/v1/mailbox/{name}/{id}
Parameters
name
- name of the mailbox the message belongs toid
- identifier of the message to retrieve- Using an
id
oflatest
will retrieve the most recent message in the mailbox
- Using an
Output
- The Inbucket version of the message headers (same as returned by List Mailbox Contents)
- The parsed SMTP headers (not converted to UTF-8)
- The decoded Text and HTML portions of the MIME body of the email
- A list of attachments
Example
Request: curl -i -H "Accept: application/json" http://localhost:9000/api/v1/mailbox/swaks/20131016T164638-0001
Response: (JSON reformatted for readability)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Expires: -1
Content-Length: 810
Date: Thu, 17 Oct 2013 21:51:59 GMT
{
"mailbox": "swaks",
"id": "20131016T164638-0001",
"from": "jamehi03@server.com",
"subject": "Swaks HTML",
"date": "2013-10-16T16:46:38.646370568-07:00",
"size": 705,
"body": {
"text": "This is a test mailing.\r\n\r\nThis should be clickable: http://google.com/\r\n",
"html": "<html>\n<body>\n<p>This is a test mailing <b>in HTML</b></p>\n\n<p>This should be clickable: [...]"
},
"header": {
"Content-Type": [
"multipart/alternative; boundary=\"----=_MIME_BOUNDARY_000_62717\""
],
"Date": [
"Wed, 16 Oct 2013 16:46:38 -0700"
],
"From": [
"jamehi03@server.com"
],
"Mime-Version": [
"1.0"
],
"Subject": [
"Swaks HTML"
],
"To": [
"swaks@inbucket.local"
]
},
"attachments": [
{
"filename": "favicon.png",
"content-type": "image/png",
"download-link": "http://localhost:9000/mailbox/dattach/swaks/20131016T164638-0001/0/favicon.png",
"view-link": "http://localhost:9000/mailbox/vattach/swaks/20131016T164638-0001/0/favicon.png",
"md5": "a72a7565b6b6587ac15fc35746307d0e"
}
]
}
Get Message Source
Retrieve the unprocessed source of a specific message.
Specification
URI
GET /api/v1/mailbox/{name}/{id}/source
Parameters
name
- name of the mailbox the message belongs toid
- identifier of the message to retrieve
Output
Plain text dump of the message headers and body in SMTP format.
Example
Request: curl -i -H "Accept: application/json" http://localhost:9000/api/v1/mailbox/swaks/20131015T161202-0000/source
Response:
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 264
Date: Tue, 15 Oct 2013 23:52:09 GMT
Date: Tue, 15 Oct 2013 16:12:02 -0700
To: swaks@inbucket.local
From: jamehi03@server.com
Subject: Swaks Plain Text
X-Mailer: swaks v20130209.0 jetmore.org/john/code/swaks/
This is a test mailing.
This should be clickable: http://google.com/
Delete Message
Delete the specified message from a mailbox.
Specification
URI
DELETE /api/v1/mailbox/{name}/{id}
Parameters
name
- name of the mailbox the message belongs toid
- identifier of the message to delete
Output
JSON encoded string "OK"
Example
Request: curl -i -H "Accept: application/json" -X DELETE http://localhost:9000/api/v1/mailbox/swaks/20131015T161202-0000
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Expires: -1
Content-Length: 5
Date: Tue, 15 Oct 2013 23:58:13 GMT
"OK"
Purge Mailbox
Delete all messages from a mailbox.
Specification
URI
DELETE /api/v1/mailbox/{name}
Parameters
name
- name of the mailbox to purge
Output
JSON encoded string "OK"
Example
Request: curl -i -H "Accept: application/json" -X DELETE http://localhost:9000/api/v1/mailbox/swaks
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Expires: -1
Content-Length: 5
Date: Tue, 15 Oct 2013 23:58:13 GMT
"OK"
Lua API
Lua scripting is the primary way to extend Inbucket's server-side functionality. Lua event handling functions can react to incoming messages, or alter the way Inbucket processes them.
Lua scripting support is new for Inbucket version 3.1.
We will work to avoid breaking changes, but given the API is incomplete, that may not be possible.
At startup, Inbucket will attempt to read the file inbucket.lua
from the
current directory. The INBUCKET_LUA_PATH
environment variable may be used to
override both the path and name of the script file.
Once the Lua script has been loaded, Inbucket will look for event handling
functions following this naming pattern: inbucket.(before|after).<event name>
.
Your First Event Handler
To play along, download and unpack an Inbucket
distribution, then change into
it's directory. You should see the inbucket
(or inbucket.exe
) program, as
well as the ui
directory.
Create a file named inbucket.lua
, which will contain a a minimal event
handler that prints the address argument passed to MAIL FROM
during an SMTP
session to STDOUT:
-- A simple "before MAIL FROM accepted" event handler.
function inbucket.before.mail_from_accepted(from_localpart, from_domain)
print(">>> LUA got MAIL FROM: ", from_localpart, from_domain)
end
Testing
Next, start Inbucket by running ./inbucket
or .\inbucket.exe
in your
terminal. Inbucket should tell you it's loading inbucket.lua:
10:44AM INF Inbucket starting buildDate=undefined phase=startup version=undefined
10:44AM INF Loading script module=lua path=inbucket.lua phase=startup
In another terminal window, use nc
(netcat), telnet
, or similar, to connect
to localhost port 2500:
- Linux or MacOS
nc localhost 2500
, ortelnet localhost 2500
- Windows
telnet localhsot 2500
(you may need to install telnet)
Then send the following SMTP commands:
HELO localhost
MAIL FROM:<user@example.com>
QUIT
In your first terminal, you should see output from your Lua script mixed in with Inbucket's normal logging:
10:45AM INF Starting SMTP session module=smtp remote=127.0.0.1:46916 session=1
>>> LUA got MAIL FROM: user example.com
10:45AM INF Mail from: user@example.com module=smtp remote=127.0.0.1:46916 session=1
Next Steps
-
The sample above used Lua's
print
function, which does not integrate well with Inbucket's formatted log output. See the Logging section to learn how to output leveled and structured logs. -
Review the Events section to learn more about the types of Inbucket events your can handle with Lua.
Logging from Lua
Inbucket allows Lua scripts to write log entries via the built-in logger
package, with an API provided by loguago.
Each logging call must include a level. Only log entries greater than or equal
to global INBUCKET_LOGLEVEL
environment variable will be output. In order of
least to most severe, the available log levels are: debug
, info
, warn
, and
error
. Inbucket will output the level info
and above by default.
Usage
logger.<level>(message, fields)
The first parameter is the log message; you may use Lua's string.format
to
interpolate values into the message, if needed. The second parameter is a table
of fields that will be included in the log entry JSON data. While you are not
required to add fields, the current API requires at least an empty table
parameter.
Example
-- Loads the logger module and makes it available via the `logger` variable.
local logger = require("logger")
-- The following functions all have the same signature but different names to
-- allow for log leveling.
logger.debug("message at debug level", {})
logger.info("message at info level", {})
logger.warn("message at warn level", {})
logger.error("message at error level", {})
-- Example with formatting and fields.
local orig_addr = "input@example.com"
local new_addr = "output@example.com"
logger.info(
-- Formats a string for the text of the log entry.
string.format("Changing address to %q", new_addr),
-- A Lua table is used for the structured portion of the entry.
{address = orig_addr})
Console log output for the example above:
$ env INBUCKET_LOGLEVEL=debug ./inbucket
1:49PM INF Loading script module=lua path=inbucket.lua phase=startup
1:49PM DBG message at debug level module=lua
1:49PM INF message at info level module=lua
1:49PM WRN message at warn level module=lua
1:49PM ERR message at error level module=lua
1:49PM INF Mapping address to "output@example.com" address=input@example.com module=lua
Example using JSON log output:
$ env INBUCKET_LOGLEVEL=debug ./inbucket -logjson
{"level":"info","module":"lua","phase":"startup","path":"inbucket.lua","time":"2023-11-13T13:54:01-08:00","message":"Loading script"}
{"level":"debug","module":"lua","time":"2023-11-13T13:54:01-08:00","message":"message at debug level"}
{"level":"info","module":"lua","time":"2023-11-13T13:54:01-08:00","message":"message at info level"}
{"level":"warn","module":"lua","time":"2023-11-13T13:54:01-08:00","message":"message at warn level"}
{"level":"error","module":"lua","time":"2023-11-13T13:54:01-08:00","message":"message at error level"}
{"level":"info","module":"lua","address":"input@example.com","time":"2023-11-13T13:54:01-08:00","message":"Mapping address to \"output@example.com\""}
Lua Events
Inbucket events can be divided into two major categories, before events and after events.
Before Events
Before events allow extensions to intercept events before Inbucket acts on them. This gives an event handler the chance to alter the processing of, or reject the event entirely.
For example, a before event handler could:
- Immediately reject a
MAIL FROM
command when the from address matches a particular pattern, returning an error code to the sending SMTP client. - Detect when a message was sent from a staging environment, and ensure it is
delivered to the
staging
mailbox in addition to the recipient's mailbox.
Before events block the active SMTP session while they are running; performing expensive operations in the handlers will reduce the perceived performance of Inbucket; SMTP clients will wait longer to deliver messages, and messages will take longer to appear in their destination mailboxes.
After Events
After events allow extensions to take an action after an event has completed.
For example, an after event handler might:
- Notify an HTTP endpoint that a new message has been received, including the subject line and senders address.
- Write metadata about a received message to disk, and then allow it to be inspected by a waiting continuous integration test by executing a shell command.
These events are processed asynchronously with respect to the rest of Inbucket's services; performing (reasonable) file or network operations within them will not degrade Inbucket's performance.
Event Flow
The order in which events occur is:
- Before MAIL FROM Accepted
- Before RCPT TO Accepted
- Before Message Stored
- After Message Stored
- After Message Deleted
Lua Event: Before MAIL FROM Accepted
This event fires when Inbucket is evaluating an SMTP MAIL FROM
command.
Example
This example denies mail that is not from james*@example.com
:
local logger = require("logger")
function inbucket.before.mail_from_accepted(session)
local from = session.from.address
logger.info(string.format("Inspecting mail from %s", from),
{ remote_ip = session.remote_addr })
-- Only allow mail from example.com, using pattern matching.
if not string.match(from, [[@example%.com$]]) then
logger.warn("Rejecting blocked origin domain", { addr = from })
-- Returning `smtp.deny()` causes the `MAIL FROM` command to be rejected.
return smtp.deny()
end
-- Only allow sending users starting with `james`.
if string.find(from, "james") ~= 1 then
logger.warn("Rejecting blocked sender", { addr = from })
-- Returning `smtp.deny(<code>, "message")` causes the `MAIL FROM` command
-- to be rejected with a custom error.
return smtp.deny(554, "We only accept mail from james")
end
-- Returning `smtp.defer()` instructs Inbucket to continue processing this
-- message, respecting it's built-in logic and environment variable config.
-- Using `smtp.allow()` would force Inbucket to accept this `MAIL FROM`.
return smtp.defer()
end
Example SMTP session
220 inbucket Inbucket SMTP ready
HELO localhost
250 Great, let's get this show on the road
MAIL FROM:<bob@example.com>
554 We only accept mail from james
Inbucket log output
1:17PM INF Starting SMTP session module=smtp remote=127.0.0.1:51726 session=1
1:17PM INF Inspecting mail from bob@example.com module=lua remoteip=127.0.0.1
1:17PM WRN Rejecting blocked sender addr=bob@example.com module=lua
1:17PM WRN Extension denied mail from <bob@example.com> module=smtp remote=127.0.0.1:51726 session=1
Lua Event: Before RCPT TO Accepted
This event fires when Inbucket is evaluating an SMTP RCPT TO
command.
Denying a particular recipient does not reject the entire message. The
previous RCPT TO
addresses will be respected, and the SMTP client may
continue at its discretion.
Example
This example denies recipient addresses with a local-part of bad
.
local logger = require("logger")
function inbucket.before.rcpt_to_accepted(session)
logger.info(string.format("Inspecting mail from %s", session.from.address), {})
-- Reject mail from "bad" user.
for _, to in ipairs(session.to) do
logger.info(string.format("Inspecting recipient %s", to.address), {})
if string.find(to.address, "bad@") == 1 then
logger.warn("Rejecting blocked recipient", { address = to.address })
-- Returning `smtp.deny(<code>, "message")` causes the `RCPT TO` command
-- to be rejected with a custom error.
return smtp.deny(554, "We don't take kindly to bad users here")
end
end
-- Returning `smtp.defer()` instructs Inbucket to continue processing this
-- message, respecting it's built-in logic and environment variable config.
-- Using `smtp.allow()` would force Inbucket to accept this `RCPT TO`.
return smtp.defer()
end
Example SMTP session
220 inbucket Inbucket SMTP ready
HELO localhost
250 Great, let's get this show on the road
MAIL FROM:<james@example.com>
250 Roger, accepting mail from <james@example.com>
RCPT TO:<good@example.com>
250 I'll make sure <good@example.com> gets this
RCPT TO:<bad@example.com>
554 We don't take kindly to bad users here
Inbucket log output
Notice that our function was called twice; once for each RCPT TO
command.
The second call received both the good@example.com
and bad@example.com
addresses in the session.to
field.
1:34PM INF Starting SMTP session module=smtp remote=127.0.0.1:40300 session=1
1:34PM INF Mail from: james@example.com module=smtp remote=127.0.0.1:40300 session=1
1:34PM INF Inspecting mail from james@example.com module=lua
1:34PM INF Inspecting recipient good@example.com module=lua
1:34PM INF Inspecting mail from james@example.com module=lua
1:34PM INF Inspecting recipient good@example.com module=lua
1:34PM INF Inspecting recipient bad@example.com module=lua
1:34PM WRN Rejecting blocked recipient address=bad@example.com module=lua
1:34PM WRN Extension denied mail to <{ bad@example.com}> module=smtp remote=127.0.0.1:40300 session=1
Lua Event: Before Message Stored
This event fires after Inbucket has accepted a message, but before it has been stored.
Example
This example changes the destination mailbox to newbox
from oldbox
, and does
not store mail destined for dropme
.
local logger = require("logger")
-- The mailbox_mapping table controls how mail is routed by this extension:
--
-- Keys (left) hold the original mailbox name.
--
-- Values (right) hold new mailbox name. A value of false causes mail for that
-- box to be discarded.
local mailbox_mapping = {
["oldbox"] = "newbox",
["dropme"] = false,
}
function inbucket.before.message_stored(msg)
local made_changes = false
local new_mailboxes = {}
-- Loop over each of the original recipient mailboxes for this message,
-- building up list of new_mailboxes.
for _, orig_box in ipairs(msg.mailboxes) do
local new_box = mailbox_mapping[orig_box]
if new_box then
logger.info(string.format("Mapping mailbox %q to %q", orig_box, new_box), {})
new_mailboxes[#new_mailboxes + 1] = new_box
made_changes = true
elseif new_box == false then
logger.warn(string.format("Discarding mail for %q", orig_box), {})
made_changes = true
else
-- No match, continue using the original value for this mailbox.
new_mailboxes[#new_mailboxes + 1] = orig_box
end
end
if made_changes then
-- Recipient mailbox list was changed, return the updated msg to Inbucket.
logger.info(
string.format("New mailboxes: %s", table.concat(new_mailboxes, ", ")),
{ count = #new_mailboxes })
msg.mailboxes = new_mailboxes
return msg
end
-- No changes, return nil to signal inbucket to use the original msg.
return nil
end
Inbucket log output:
4:36PM INF Starting SMTP session module=smtp remote=127.0.0.1:52446 session=2
4:36PM INF Mail from: james@localhost module=smtp remote=127.0.0.1:52446 session=2
4:36PM INF Mapping mailbox "oldbox" to "newbox" module=lua
4:36PM WRN Discarding mail for "dropme" module=lua
4:36PM INF New mailboxes: newbox count=1 module=lua
4:36PM INF Message size 312 bytes module=smtp remote=127.0.0.1:52446 session=2
Lua Event: After Message Stored
This event fires after Inbucket has accepted and stored a message.
Examples
Print Metadata
This example prints the metadata of stored messages to the console. Using
print()
instead of logging is not advised, but this demonstrates the fields
available on the message paramter.
function inbucket.after.message_stored(msg)
print("\n## message stored ##")
print(string.format("mailbox: %s", msg.mailbox))
print(string.format("id: %s", msg.id))
print(string.format("from: %q <%s>", msg.from.name, msg.from.address))
-- Loop over each To address.
for i, to in ipairs(msg.to) do
print(string.format("to[%s]: %q <%s>", i, to.name, to.address))
end
-- Format the unix timestamp in a human readable way.
print(string.format("date: %s", os.date("%c", msg.date)))
print(string.format("size: %s", msg.size))
print(string.format("subject: %s", msg.subject))
end
Example output:
## message stored ##
mailbox: test
id: 8
from: "" <james@localhost>
to[1]: "" <test@inbucket.local>
date: 22 Mar 24 15:09 PDT
subject: Test from Outlook
Webhook style HTTP post as JSON
In this example, message metadata is converted to JSON and a POST request is made to an external web service:
local http = require("http")
local json = require("json")
BASE_URL = "https://myapi.example.com"
function inbucket.after.message_stored(msg)
local data = {
sender = string.format("Mail from %q", msg.from.address),
subject = msg.subject
}
-- Encode the data table above as JSON.
local body = json.encode(data)
-- POST JSON or fail.
assert(http.post(BASE_URL .. "/notify/text", {
headers = { ["Content-Type"] = "application/json" },
body = body,
}))
end
Example JSON body:
{"subject":"Test from Outlook","sender":"Mail from \"james@localhost\""}
Write to file
An example that may be relevant for continuous integration testing. It writes metadata to temporary file and runs external shell command on it:
function inbucket.after.message_stored(msg)
local content = string.format("%q,%q\n", msg.from.address, msg.subject)
-- Write content to temporary file.
local fnam = os.tmpname()
local f = assert(io.open(fnam, "w+"))
assert(f:write(content))
f:close()
local cmd = string.format("cat %q", fnam)
print(string.format("\n### running %s ###", cmd))
local status = os.execute(cmd)
if status ~= 0 then
error("command failed: " .. cmd)
end
end
Example output:
### running cat "/tmp/63708877" ###
"james@localhost","Test from Outlook"
Lua Event: After Message Deleted
This example logs some information when a message is deleted:
local logger = require("logger")
function inbucket.after.message_deleted(msg)
logger.warn(string.format("Deleted ID %s (subj %q)", msg.id, msg.subject),
{ mailbox = msg.mailbox })
end
Example output:
10:14AM WRN Deleted ID 7 (subj "Test from Gmail") mailbox=test module=lua