mayai-pec-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mayai_pec_cli-0.1.0/.gitignore +46 -0
- mayai_pec_cli-0.1.0/LICENSE +21 -0
- mayai_pec_cli-0.1.0/Makefile +20 -0
- mayai_pec_cli-0.1.0/PKG-INFO +261 -0
- mayai_pec_cli-0.1.0/README.md +213 -0
- mayai_pec_cli-0.1.0/pec_cli/__init__.py +3 -0
- mayai_pec_cli-0.1.0/pec_cli/auth/__init__.py +21 -0
- mayai_pec_cli-0.1.0/pec_cli/auth/credentials.py +251 -0
- mayai_pec_cli-0.1.0/pec_cli/daticert.py +161 -0
- mayai_pec_cli-0.1.0/pec_cli/imap/__init__.py +5 -0
- mayai_pec_cli-0.1.0/pec_cli/imap/client.py +400 -0
- mayai_pec_cli-0.1.0/pec_cli/main.py +463 -0
- mayai_pec_cli-0.1.0/pec_cli/models/__init__.py +5 -0
- mayai_pec_cli-0.1.0/pec_cli/models/message.py +112 -0
- mayai_pec_cli-0.1.0/pec_cli/output/__init__.py +5 -0
- mayai_pec_cli-0.1.0/pec_cli/output/formatter.py +69 -0
- mayai_pec_cli-0.1.0/pec_cli/smtp/__init__.py +5 -0
- mayai_pec_cli-0.1.0/pec_cli/smtp/sender.py +82 -0
- mayai_pec_cli-0.1.0/pyproject.toml +102 -0
- mayai_pec_cli-0.1.0/tests/__init__.py +0 -0
- mayai_pec_cli-0.1.0/tests/test_smoke.py +288 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
*.egg
|
|
13
|
+
pip-wheel-metadata/
|
|
14
|
+
|
|
15
|
+
# Virtual environments
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
ENV/
|
|
20
|
+
|
|
21
|
+
# Test / lint caches
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
|
|
28
|
+
# Editors / OS
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
.DS_Store
|
|
33
|
+
|
|
34
|
+
# Local Claude / agent state
|
|
35
|
+
.claude/
|
|
36
|
+
|
|
37
|
+
# Credentials and local config (never commit PEC passwords or Fernet keys)
|
|
38
|
+
credentials.json
|
|
39
|
+
*.credentials.json
|
|
40
|
+
key.bin
|
|
41
|
+
.env
|
|
42
|
+
.env.*
|
|
43
|
+
config.json
|
|
44
|
+
|
|
45
|
+
# Attachments downloaded by `pec get --save-attachments`
|
|
46
|
+
attachments/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MayAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.PHONY: install dev test lint clean
|
|
2
|
+
|
|
3
|
+
PYTHON ?= python3
|
|
4
|
+
|
|
5
|
+
install:
|
|
6
|
+
$(PYTHON) -m pip install -e .
|
|
7
|
+
|
|
8
|
+
dev:
|
|
9
|
+
$(PYTHON) -m pip install -e ".[dev]"
|
|
10
|
+
|
|
11
|
+
test:
|
|
12
|
+
$(PYTHON) -m pytest tests/
|
|
13
|
+
|
|
14
|
+
lint:
|
|
15
|
+
$(PYTHON) -m ruff check pec_cli/
|
|
16
|
+
|
|
17
|
+
clean:
|
|
18
|
+
find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
|
19
|
+
find . -type f -name '*.pyc' -delete
|
|
20
|
+
rm -rf build dist *.egg-info .pytest_cache .ruff_cache
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mayai-pec-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Command-line client for Italian PEC (Posta Elettronica Certificata) — built for AI agents and developers.
|
|
5
|
+
Project-URL: Homepage, https://mayai.it
|
|
6
|
+
Project-URL: Documentation, https://github.com/mayai-it/pec-cli#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/mayai-it/pec-cli
|
|
8
|
+
Project-URL: Issues, https://github.com/mayai-it/pec-cli/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/mayai-it/pec-cli/releases
|
|
10
|
+
Author-email: MayAI <info@mayai.it>
|
|
11
|
+
Maintainer-email: MayAI <info@mayai.it>
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: ai-agents,aruba,cli,command-line,email,imap,infocert,italian,italy,legalmail,llm,namirial,pec,posta-elettronica-certificata,poste-italiane,smtp
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Intended Audience :: Legal Industry
|
|
19
|
+
Classifier: Intended Audience :: System Administrators
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Natural Language :: English
|
|
22
|
+
Classifier: Natural Language :: Italian
|
|
23
|
+
Classifier: Operating System :: MacOS
|
|
24
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
27
|
+
Classifier: Programming Language :: Python :: 3
|
|
28
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Topic :: Communications :: Email
|
|
33
|
+
Classifier: Topic :: Communications :: Email :: Post-Office :: IMAP
|
|
34
|
+
Classifier: Topic :: Communications :: Email :: Post-Office :: POP3
|
|
35
|
+
Classifier: Topic :: Office/Business
|
|
36
|
+
Classifier: Topic :: Utilities
|
|
37
|
+
Classifier: Typing :: Typed
|
|
38
|
+
Requires-Python: >=3.11
|
|
39
|
+
Requires-Dist: click>=8.1.0
|
|
40
|
+
Requires-Dist: cryptography>=42.0.0
|
|
41
|
+
Requires-Dist: keyring>=24.0.0
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: build>=1.2.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: twine>=5.0.0; extra == 'dev'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# pec-cli
|
|
50
|
+
|
|
51
|
+
Command-line client for **PEC** (Posta Elettronica Certificata — Italian
|
|
52
|
+
certified email), built for both humans and AI agents. Designed to be
|
|
53
|
+
context-efficient: the default output strips empty fields, and `--json`
|
|
54
|
+
produces NDJSON suitable for piping into LLMs or jq.
|
|
55
|
+
|
|
56
|
+
Talks to the standard IMAP/SMTP endpoints exposed by Italian PEC providers
|
|
57
|
+
(Aruba, Legalmail/InfoCert, Namirial, Register.it, Poste Italiane, Pec.it),
|
|
58
|
+
all over SSL/TLS.
|
|
59
|
+
|
|
60
|
+
Part of [MayAI CLI](https://mayai.it).
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
- Python 3.11+
|
|
65
|
+
- A working PEC account from one of the supported providers
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
From PyPI (recommended):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install mayai-pec-cli
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The package installs a single `pec` command on your `PATH`.
|
|
76
|
+
|
|
77
|
+
From source:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/mayai-it/pec-cli.git
|
|
81
|
+
cd pec-cli
|
|
82
|
+
make install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For local development (adds `pytest`, `ruff`):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
make dev
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Quick start
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 1. Authenticate (password prompted interactively, never passed as a flag)
|
|
95
|
+
pec auth login --address mia@pec.it --provider aruba
|
|
96
|
+
|
|
97
|
+
# 2. Verify
|
|
98
|
+
pec auth status
|
|
99
|
+
|
|
100
|
+
# 3. List the 20 most recent PECs in the inbox as NDJSON
|
|
101
|
+
pec --json list
|
|
102
|
+
|
|
103
|
+
# 4. Filter to unread, since a given date
|
|
104
|
+
pec --json list --unread --from 2025-01-01 --limit 50
|
|
105
|
+
|
|
106
|
+
# 5. Read a single message and save its attachments to ./attachments
|
|
107
|
+
pec get 1234 --save-attachments ./attachments
|
|
108
|
+
|
|
109
|
+
# 6. Inspect the parsed PEC certification (daticert.xml) for a message
|
|
110
|
+
pec get 1234 --cert --json
|
|
111
|
+
|
|
112
|
+
# 7. Trace the full receipt chain for an original message id
|
|
113
|
+
pec trace 'opec123.20260321102500.12345.67.1.1@pec.it'
|
|
114
|
+
|
|
115
|
+
# 8. Send a PEC with an attachment
|
|
116
|
+
pec send --to dest@pec.it --subject "Oggetto" --file body.txt --attach doc.pdf
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Command reference
|
|
120
|
+
|
|
121
|
+
| Command | Description |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `pec auth login --address ADDR --provider P` | Prompt for password, verify via IMAP, save credentials in the system keyring (Fernet-encrypted file as fallback). |
|
|
124
|
+
| `pec auth status` | Show whether credentials are present. |
|
|
125
|
+
| `pec auth logout` | Delete saved credentials (keyring entry + any local encryption key). |
|
|
126
|
+
| `pec list [--folder F] [--unread] [--from YYYY-MM-DD] [--limit N]` | List PEC messages (default folder `inbox`, default limit 20). |
|
|
127
|
+
| `pec get <id> [--folder F] [--save-attachments DIR] [--cert]` | Fetch a single PEC by IMAP UID; `--cert` includes the parsed `daticert.xml` certification; `--save-attachments` writes attachments to `DIR`. |
|
|
128
|
+
| `pec trace <message-id> [--folder F] [--limit N]` | Find every receipt in the folder whose `daticert.xml` references this message id, ordered chronologically (`accettazione` → `presa-in-carico` → `avvenuta-consegna` / `errore-consegna`). |
|
|
129
|
+
| `pec send --to ADDR --subject S (--body T | --file F) [--attach F] [--cc ADDR] [--dry-run]` | Send a PEC; `--to`, `--cc`, `--attach` are repeatable. |
|
|
130
|
+
|
|
131
|
+
### Global flags
|
|
132
|
+
|
|
133
|
+
These work in any position (before or after the subcommand):
|
|
134
|
+
|
|
135
|
+
| Flag | Effect |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `--json` | Emit one JSON object per line (NDJSON). |
|
|
138
|
+
| `--verbose` | Log IMAP/SMTP timings and certification metadata to stderr. |
|
|
139
|
+
| `-h`, `--help` | Show help for the current command. |
|
|
140
|
+
|
|
141
|
+
### Exit codes
|
|
142
|
+
|
|
143
|
+
| Code | Meaning |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `0` | Success |
|
|
146
|
+
| `1` | Application error (network, send failure, bad arguments) |
|
|
147
|
+
| `2` | Not authenticated — run `pec auth login` |
|
|
148
|
+
|
|
149
|
+
## Supported providers
|
|
150
|
+
|
|
151
|
+
| Provider | `--provider` | IMAP | SMTP |
|
|
152
|
+
|----------------------|--------------|-------------------------------|-------------------------------|
|
|
153
|
+
| Aruba PEC | `aruba` | `imaps.pec.aruba.it:993` | `smtps.pec.aruba.it:465` |
|
|
154
|
+
| Legalmail (InfoCert) | `legalmail` | `imapmail.legalmail.it:993` | `smtpmail.legalmail.it:465` |
|
|
155
|
+
| Namirial | `namirial` | `imap.namirialpec.it:993` | `smtp.namirialpec.it:465` |
|
|
156
|
+
| Register.it | `register` | `imap.pec.register.it:993` | `smtp.pec.register.it:465` |
|
|
157
|
+
| Poste Italiane | `poste` | `imappec.poste.it:993` | `smtppec.poste.it:465` |
|
|
158
|
+
| Pec.it | `pec.it` | `imap.pec.it:993` | `smtp.pec.it:465` |
|
|
159
|
+
|
|
160
|
+
All providers use implicit SSL/TLS (IMAPS:993 / SMTPS:465). Username is the
|
|
161
|
+
full PEC address; the password is the one provided by the PEC provider.
|
|
162
|
+
|
|
163
|
+
## Authentication
|
|
164
|
+
|
|
165
|
+
PEC is plain IMAP/SMTP with SSL — there's no OAuth. `pec auth login`:
|
|
166
|
+
|
|
167
|
+
1. Prompts you for the password on stderr (never echoed, never on argv).
|
|
168
|
+
2. Verifies it by opening an IMAP connection and logging in.
|
|
169
|
+
3. Stores the password in the **system keyring** — macOS Keychain, Linux
|
|
170
|
+
Secret Service, or Windows Credential Locker (DPAPI) — under the service
|
|
171
|
+
name `mayai-cli-pec` and the PEC address as the username.
|
|
172
|
+
4. Writes a small metadata file at
|
|
173
|
+
`~/.config/mayai-cli/pec/credentials.json` (mode `0600`) recording the
|
|
174
|
+
address, provider, and where the password lives.
|
|
175
|
+
|
|
176
|
+
On headless boxes or CI where no keyring backend is available, the CLI
|
|
177
|
+
transparently falls back to **Fernet** encryption: a 32-byte key at
|
|
178
|
+
`~/.config/mayai-cli/pec/key.bin` (mode `0600`) encrypts the password inside
|
|
179
|
+
`credentials.json`. Existing installs that still have a `key.bin` are
|
|
180
|
+
migrated to the keyring on the next `pec auth login` (and `key.bin` is then
|
|
181
|
+
removed).
|
|
182
|
+
|
|
183
|
+
`pec auth logout` clears the keyring entry and removes the on-disk files.
|
|
184
|
+
The password is never written to plain disk and never accepted via a
|
|
185
|
+
command-line flag.
|
|
186
|
+
|
|
187
|
+
## Output format
|
|
188
|
+
|
|
189
|
+
- **Default** — compact human-readable text. Empty / null fields are stripped
|
|
190
|
+
so terminal output stays scannable.
|
|
191
|
+
- **`--json`** — NDJSON. One object per line; lists stream one element per
|
|
192
|
+
line so consumers can process incrementally.
|
|
193
|
+
- **`--verbose`** — adds protocol timing lines on stderr (e.g.
|
|
194
|
+
`imap: connected to imaps.pec.aruba.it:993 as mia@pec.it (284ms)`), and
|
|
195
|
+
surfaces the PEC certification attachments (`daticert.xml`,
|
|
196
|
+
`postacert.eml`, `smime.p7s/p7m`) that are normally hidden.
|
|
197
|
+
|
|
198
|
+
Errors always go to stderr, prefixed with `error:`.
|
|
199
|
+
|
|
200
|
+
### What `pec list` returns
|
|
201
|
+
|
|
202
|
+
Each row carries the IMAP UID (`id`), a normalized ISO date, the sender, the
|
|
203
|
+
subject, the PEC type (`accettazione`, `consegna`, `errore`, `preavviso`, …)
|
|
204
|
+
when present, and read/attachment flags.
|
|
205
|
+
|
|
206
|
+
### What `pec get` returns
|
|
207
|
+
|
|
208
|
+
The full message — `from`, `to`, `cc`, `subject`, `date`, plain-text body
|
|
209
|
+
(HTML body too with `--verbose`), and the attachment list with each
|
|
210
|
+
attachment's filename and size in bytes.
|
|
211
|
+
|
|
212
|
+
PEC messages that carry a `daticert.xml` certification (every receipt and
|
|
213
|
+
every sent PEC) get an extra `pec_cert_type` field in the default output,
|
|
214
|
+
e.g. `"avvenuta-consegna"`. Pass `--cert` to also include the fully parsed
|
|
215
|
+
certification (`tipo`, `mittente`, `destinatari`, `data`, `identificativo`,
|
|
216
|
+
`riferimento_message_id`, `oggetto`, optional `errore`):
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
pec get 1234 --cert --json
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
By default the PEC certification files (`daticert.xml`, `postacert.eml`,
|
|
223
|
+
`smime.p7s`, `smime.p7m`) are filtered out of both the listed attachments
|
|
224
|
+
and the saved files; pass `--verbose` to include them. Use
|
|
225
|
+
`--save-attachments DIR` to write attachments to disk under `DIR`
|
|
226
|
+
(created if it doesn't exist).
|
|
227
|
+
|
|
228
|
+
### What `pec trace` returns
|
|
229
|
+
|
|
230
|
+
`pec trace <message-id>` scans recent PECs in a folder (default `inbox`,
|
|
231
|
+
`--limit 200`), reads each `daticert.xml`, and returns the chain whose
|
|
232
|
+
`riferimento_message_id` matches the given id, sorted chronologically:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"message_id": "opec123.20260321102500.12345.67.1.1@pec.it",
|
|
237
|
+
"events": [
|
|
238
|
+
{"id": "204", "tipo": "accettazione", "data": "2026-03-21T10:25:00+01:00", "...": "..."},
|
|
239
|
+
{"id": "205", "tipo": "presa-in-carico", "data": "2026-03-21T10:25:04+01:00", "...": "..."},
|
|
240
|
+
{"id": "206", "tipo": "avvenuta-consegna","data": "2026-03-21T10:25:07+01:00", "...": "..."}
|
|
241
|
+
],
|
|
242
|
+
"count": 3
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The argument is the certified `identificativo` of the original message — the
|
|
247
|
+
same value `pec get --cert` returns under `pec_cert.identificativo`. Surround
|
|
248
|
+
or strip `<...>` brackets as you wish; the CLI normalizes them.
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
make dev # install with dev extras
|
|
254
|
+
make test # run pytest
|
|
255
|
+
make lint # run ruff
|
|
256
|
+
make clean # remove caches and build artifacts
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# pec-cli
|
|
2
|
+
|
|
3
|
+
Command-line client for **PEC** (Posta Elettronica Certificata — Italian
|
|
4
|
+
certified email), built for both humans and AI agents. Designed to be
|
|
5
|
+
context-efficient: the default output strips empty fields, and `--json`
|
|
6
|
+
produces NDJSON suitable for piping into LLMs or jq.
|
|
7
|
+
|
|
8
|
+
Talks to the standard IMAP/SMTP endpoints exposed by Italian PEC providers
|
|
9
|
+
(Aruba, Legalmail/InfoCert, Namirial, Register.it, Poste Italiane, Pec.it),
|
|
10
|
+
all over SSL/TLS.
|
|
11
|
+
|
|
12
|
+
Part of [MayAI CLI](https://mayai.it).
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Python 3.11+
|
|
17
|
+
- A working PEC account from one of the supported providers
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
From PyPI (recommended):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install mayai-pec-cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package installs a single `pec` command on your `PATH`.
|
|
28
|
+
|
|
29
|
+
From source:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/mayai-it/pec-cli.git
|
|
33
|
+
cd pec-cli
|
|
34
|
+
make install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For local development (adds `pytest`, `ruff`):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
make dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 1. Authenticate (password prompted interactively, never passed as a flag)
|
|
47
|
+
pec auth login --address mia@pec.it --provider aruba
|
|
48
|
+
|
|
49
|
+
# 2. Verify
|
|
50
|
+
pec auth status
|
|
51
|
+
|
|
52
|
+
# 3. List the 20 most recent PECs in the inbox as NDJSON
|
|
53
|
+
pec --json list
|
|
54
|
+
|
|
55
|
+
# 4. Filter to unread, since a given date
|
|
56
|
+
pec --json list --unread --from 2025-01-01 --limit 50
|
|
57
|
+
|
|
58
|
+
# 5. Read a single message and save its attachments to ./attachments
|
|
59
|
+
pec get 1234 --save-attachments ./attachments
|
|
60
|
+
|
|
61
|
+
# 6. Inspect the parsed PEC certification (daticert.xml) for a message
|
|
62
|
+
pec get 1234 --cert --json
|
|
63
|
+
|
|
64
|
+
# 7. Trace the full receipt chain for an original message id
|
|
65
|
+
pec trace 'opec123.20260321102500.12345.67.1.1@pec.it'
|
|
66
|
+
|
|
67
|
+
# 8. Send a PEC with an attachment
|
|
68
|
+
pec send --to dest@pec.it --subject "Oggetto" --file body.txt --attach doc.pdf
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Command reference
|
|
72
|
+
|
|
73
|
+
| Command | Description |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `pec auth login --address ADDR --provider P` | Prompt for password, verify via IMAP, save credentials in the system keyring (Fernet-encrypted file as fallback). |
|
|
76
|
+
| `pec auth status` | Show whether credentials are present. |
|
|
77
|
+
| `pec auth logout` | Delete saved credentials (keyring entry + any local encryption key). |
|
|
78
|
+
| `pec list [--folder F] [--unread] [--from YYYY-MM-DD] [--limit N]` | List PEC messages (default folder `inbox`, default limit 20). |
|
|
79
|
+
| `pec get <id> [--folder F] [--save-attachments DIR] [--cert]` | Fetch a single PEC by IMAP UID; `--cert` includes the parsed `daticert.xml` certification; `--save-attachments` writes attachments to `DIR`. |
|
|
80
|
+
| `pec trace <message-id> [--folder F] [--limit N]` | Find every receipt in the folder whose `daticert.xml` references this message id, ordered chronologically (`accettazione` → `presa-in-carico` → `avvenuta-consegna` / `errore-consegna`). |
|
|
81
|
+
| `pec send --to ADDR --subject S (--body T | --file F) [--attach F] [--cc ADDR] [--dry-run]` | Send a PEC; `--to`, `--cc`, `--attach` are repeatable. |
|
|
82
|
+
|
|
83
|
+
### Global flags
|
|
84
|
+
|
|
85
|
+
These work in any position (before or after the subcommand):
|
|
86
|
+
|
|
87
|
+
| Flag | Effect |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `--json` | Emit one JSON object per line (NDJSON). |
|
|
90
|
+
| `--verbose` | Log IMAP/SMTP timings and certification metadata to stderr. |
|
|
91
|
+
| `-h`, `--help` | Show help for the current command. |
|
|
92
|
+
|
|
93
|
+
### Exit codes
|
|
94
|
+
|
|
95
|
+
| Code | Meaning |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `0` | Success |
|
|
98
|
+
| `1` | Application error (network, send failure, bad arguments) |
|
|
99
|
+
| `2` | Not authenticated — run `pec auth login` |
|
|
100
|
+
|
|
101
|
+
## Supported providers
|
|
102
|
+
|
|
103
|
+
| Provider | `--provider` | IMAP | SMTP |
|
|
104
|
+
|----------------------|--------------|-------------------------------|-------------------------------|
|
|
105
|
+
| Aruba PEC | `aruba` | `imaps.pec.aruba.it:993` | `smtps.pec.aruba.it:465` |
|
|
106
|
+
| Legalmail (InfoCert) | `legalmail` | `imapmail.legalmail.it:993` | `smtpmail.legalmail.it:465` |
|
|
107
|
+
| Namirial | `namirial` | `imap.namirialpec.it:993` | `smtp.namirialpec.it:465` |
|
|
108
|
+
| Register.it | `register` | `imap.pec.register.it:993` | `smtp.pec.register.it:465` |
|
|
109
|
+
| Poste Italiane | `poste` | `imappec.poste.it:993` | `smtppec.poste.it:465` |
|
|
110
|
+
| Pec.it | `pec.it` | `imap.pec.it:993` | `smtp.pec.it:465` |
|
|
111
|
+
|
|
112
|
+
All providers use implicit SSL/TLS (IMAPS:993 / SMTPS:465). Username is the
|
|
113
|
+
full PEC address; the password is the one provided by the PEC provider.
|
|
114
|
+
|
|
115
|
+
## Authentication
|
|
116
|
+
|
|
117
|
+
PEC is plain IMAP/SMTP with SSL — there's no OAuth. `pec auth login`:
|
|
118
|
+
|
|
119
|
+
1. Prompts you for the password on stderr (never echoed, never on argv).
|
|
120
|
+
2. Verifies it by opening an IMAP connection and logging in.
|
|
121
|
+
3. Stores the password in the **system keyring** — macOS Keychain, Linux
|
|
122
|
+
Secret Service, or Windows Credential Locker (DPAPI) — under the service
|
|
123
|
+
name `mayai-cli-pec` and the PEC address as the username.
|
|
124
|
+
4. Writes a small metadata file at
|
|
125
|
+
`~/.config/mayai-cli/pec/credentials.json` (mode `0600`) recording the
|
|
126
|
+
address, provider, and where the password lives.
|
|
127
|
+
|
|
128
|
+
On headless boxes or CI where no keyring backend is available, the CLI
|
|
129
|
+
transparently falls back to **Fernet** encryption: a 32-byte key at
|
|
130
|
+
`~/.config/mayai-cli/pec/key.bin` (mode `0600`) encrypts the password inside
|
|
131
|
+
`credentials.json`. Existing installs that still have a `key.bin` are
|
|
132
|
+
migrated to the keyring on the next `pec auth login` (and `key.bin` is then
|
|
133
|
+
removed).
|
|
134
|
+
|
|
135
|
+
`pec auth logout` clears the keyring entry and removes the on-disk files.
|
|
136
|
+
The password is never written to plain disk and never accepted via a
|
|
137
|
+
command-line flag.
|
|
138
|
+
|
|
139
|
+
## Output format
|
|
140
|
+
|
|
141
|
+
- **Default** — compact human-readable text. Empty / null fields are stripped
|
|
142
|
+
so terminal output stays scannable.
|
|
143
|
+
- **`--json`** — NDJSON. One object per line; lists stream one element per
|
|
144
|
+
line so consumers can process incrementally.
|
|
145
|
+
- **`--verbose`** — adds protocol timing lines on stderr (e.g.
|
|
146
|
+
`imap: connected to imaps.pec.aruba.it:993 as mia@pec.it (284ms)`), and
|
|
147
|
+
surfaces the PEC certification attachments (`daticert.xml`,
|
|
148
|
+
`postacert.eml`, `smime.p7s/p7m`) that are normally hidden.
|
|
149
|
+
|
|
150
|
+
Errors always go to stderr, prefixed with `error:`.
|
|
151
|
+
|
|
152
|
+
### What `pec list` returns
|
|
153
|
+
|
|
154
|
+
Each row carries the IMAP UID (`id`), a normalized ISO date, the sender, the
|
|
155
|
+
subject, the PEC type (`accettazione`, `consegna`, `errore`, `preavviso`, …)
|
|
156
|
+
when present, and read/attachment flags.
|
|
157
|
+
|
|
158
|
+
### What `pec get` returns
|
|
159
|
+
|
|
160
|
+
The full message — `from`, `to`, `cc`, `subject`, `date`, plain-text body
|
|
161
|
+
(HTML body too with `--verbose`), and the attachment list with each
|
|
162
|
+
attachment's filename and size in bytes.
|
|
163
|
+
|
|
164
|
+
PEC messages that carry a `daticert.xml` certification (every receipt and
|
|
165
|
+
every sent PEC) get an extra `pec_cert_type` field in the default output,
|
|
166
|
+
e.g. `"avvenuta-consegna"`. Pass `--cert` to also include the fully parsed
|
|
167
|
+
certification (`tipo`, `mittente`, `destinatari`, `data`, `identificativo`,
|
|
168
|
+
`riferimento_message_id`, `oggetto`, optional `errore`):
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pec get 1234 --cert --json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
By default the PEC certification files (`daticert.xml`, `postacert.eml`,
|
|
175
|
+
`smime.p7s`, `smime.p7m`) are filtered out of both the listed attachments
|
|
176
|
+
and the saved files; pass `--verbose` to include them. Use
|
|
177
|
+
`--save-attachments DIR` to write attachments to disk under `DIR`
|
|
178
|
+
(created if it doesn't exist).
|
|
179
|
+
|
|
180
|
+
### What `pec trace` returns
|
|
181
|
+
|
|
182
|
+
`pec trace <message-id>` scans recent PECs in a folder (default `inbox`,
|
|
183
|
+
`--limit 200`), reads each `daticert.xml`, and returns the chain whose
|
|
184
|
+
`riferimento_message_id` matches the given id, sorted chronologically:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"message_id": "opec123.20260321102500.12345.67.1.1@pec.it",
|
|
189
|
+
"events": [
|
|
190
|
+
{"id": "204", "tipo": "accettazione", "data": "2026-03-21T10:25:00+01:00", "...": "..."},
|
|
191
|
+
{"id": "205", "tipo": "presa-in-carico", "data": "2026-03-21T10:25:04+01:00", "...": "..."},
|
|
192
|
+
{"id": "206", "tipo": "avvenuta-consegna","data": "2026-03-21T10:25:07+01:00", "...": "..."}
|
|
193
|
+
],
|
|
194
|
+
"count": 3
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The argument is the certified `identificativo` of the original message — the
|
|
199
|
+
same value `pec get --cert` returns under `pec_cert.identificativo`. Surround
|
|
200
|
+
or strip `<...>` brackets as you wish; the CLI normalizes them.
|
|
201
|
+
|
|
202
|
+
## Development
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
make dev # install with dev extras
|
|
206
|
+
make test # run pytest
|
|
207
|
+
make lint # run ruff
|
|
208
|
+
make clean # remove caches and build artifacts
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Credential storage for PEC accounts."""
|
|
2
|
+
|
|
3
|
+
from pec_cli.auth.credentials import (
|
|
4
|
+
PROVIDERS,
|
|
5
|
+
Credentials,
|
|
6
|
+
ProviderConfig,
|
|
7
|
+
delete_credentials,
|
|
8
|
+
get_provider,
|
|
9
|
+
load_credentials,
|
|
10
|
+
save_credentials,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Credentials",
|
|
15
|
+
"PROVIDERS",
|
|
16
|
+
"ProviderConfig",
|
|
17
|
+
"delete_credentials",
|
|
18
|
+
"get_provider",
|
|
19
|
+
"load_credentials",
|
|
20
|
+
"save_credentials",
|
|
21
|
+
]
|