freemyid 0.2.0.dev1__py3-none-any.whl
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.
- freemyid/__init__.py +2 -0
- freemyid/_data/brokers/LICENSE-DATA.md +28 -0
- freemyid/_data/brokers/registry.sample.yaml +674 -0
- freemyid/_data/brokers/state_laws.yaml +448 -0
- freemyid/_data/schema/registry.schema.json +668 -0
- freemyid/_data/schema/setup-config.schema.json +538 -0
- freemyid/_data/schema/state_laws.schema.json +222 -0
- freemyid/cli.py +725 -0
- freemyid/cli_json.py +198 -0
- freemyid/config.py +43 -0
- freemyid/consent.py +82 -0
- freemyid/crypto.py +197 -0
- freemyid/doctor.py +374 -0
- freemyid/engine/__init__.py +0 -0
- freemyid/engine/_dev/__init__.py +101 -0
- freemyid/engine/_dev/devrun.py +101 -0
- freemyid/engine/_dev/inbox/devauto.eml +7 -0
- freemyid/engine/_dev/registry.yaml +106 -0
- freemyid/engine/_dev/site/devauto.example/confirm.html +1 -0
- freemyid/engine/_dev/site/devauto.example/name/jane-testperson/il.html +17 -0
- freemyid/engine/_dev/site/devauto.example/name/jane-testperson/nc.html +4 -0
- freemyid/engine/_dev/site/devauto.example/name/jane-testperson/oh.html +4 -0
- freemyid/engine/_dev/site/devauto.example/optout/done.html +1 -0
- freemyid/engine/_dev/site/devauto.example/optout.html +6 -0
- freemyid/engine/_dev/site/devblocked.example/name/jane-testperson/il.html +3 -0
- freemyid/engine/_dev/site/devblocked.example/optout.html +3 -0
- freemyid/engine/_dev/site/devfill.example/ajax.html +20 -0
- freemyid/engine/_dev/site/devfill.example/aria.html +16 -0
- freemyid/engine/_dev/site/devfill.example/consent.html +20 -0
- freemyid/engine/_dev/site/devfill.example/done.html +3 -0
- freemyid/engine/_dev/site/devfill.example/empty.html +1 -0
- freemyid/engine/_dev/site/devfill.example/fullname.html +12 -0
- freemyid/engine/_dev/site/devfill.example/labels.html +32 -0
- freemyid/engine/_dev/site/devfill.example/late.html +22 -0
- freemyid/engine/_dev/site/devfind.example/hostile.html +12 -0
- freemyid/engine/_dev/site/devfind.example/p/jane-testperson-shelbyville-il.html +6 -0
- freemyid/engine/_dev/site/devfind.example/p/jane-testperson-springfield-il.html +6 -0
- freemyid/engine/_dev/site/devfind.example/results.html +11 -0
- freemyid/engine/_dev/site/devfind.example/search.html +16 -0
- freemyid/engine/_dev/site/devweb.example/remove.html +1 -0
- freemyid/engine/browser.py +430 -0
- freemyid/engine/fill_for_me.py +630 -0
- freemyid/engine/filler.py +523 -0
- freemyid/engine/handoff.py +237 -0
- freemyid/engine/letters.py +125 -0
- freemyid/engine/listing_finder.py +538 -0
- freemyid/engine/mail.py +406 -0
- freemyid/engine/mail_tls.py +198 -0
- freemyid/engine/matcher.py +208 -0
- freemyid/engine/outbox.py +421 -0
- freemyid/engine/plugins.py +68 -0
- freemyid/engine/runner.py +791 -0
- freemyid/engine/tiers.py +107 -0
- freemyid/interfaces.py +164 -0
- freemyid/logging_setup.py +152 -0
- freemyid/models.py +700 -0
- freemyid/netpolicy.py +145 -0
- freemyid/notify.py +58 -0
- freemyid/paths.py +83 -0
- freemyid/registry.py +275 -0
- freemyid/scheduler/__init__.py +203 -0
- freemyid/scheduler/render.py +91 -0
- freemyid/secret_backends.py +562 -0
- freemyid/secrets.py +25 -0
- freemyid/setup_config.py +311 -0
- freemyid/state.py +151 -0
- freemyid/storage/__init__.py +3 -0
- freemyid/storage/db.py +394 -0
- freemyid/submitters/__init__.py +0 -0
- freemyid/submitters/assisted.py +171 -0
- freemyid/submitters/base.py +119 -0
- freemyid/submitters/email.py +40 -0
- freemyid/submitters/web_form.py +276 -0
- freemyid/templates/_identity.txt.j2 +19 -0
- freemyid/templates/_signature.txt.j2 +7 -0
- freemyid/templates/ccpa.txt.j2 +16 -0
- freemyid/templates/follow_up.txt.j2 +15 -0
- freemyid/templates/generic_removal.txt.j2 +12 -0
- freemyid/templates/legal_demand.txt.j2 +20 -0
- freemyid/templates/phone_script.txt.j2 +25 -0
- freemyid/templates/postal_letter.txt.j2 +20 -0
- freemyid/templates/state_privacy.txt.j2 +17 -0
- freemyid/wizard/__init__.py +0 -0
- freemyid/wizard/dashboard.py +401 -0
- freemyid/wizard/desktop.py +188 -0
- freemyid/wizard/draft.py +627 -0
- freemyid/wizard/firstrun.py +42 -0
- freemyid/wizard/flows.py +283 -0
- freemyid/wizard/report.py +86 -0
- freemyid/wizard/server.py +834 -0
- freemyid/wizard/static/__init__.py +0 -0
- freemyid/wizard/static/app.js +98 -0
- freemyid/wizard/static/freemyid.png +0 -0
- freemyid/wizard/static/style.css +85 -0
- freemyid/wizard/ui.py +343 -0
- freemyid-0.2.0.dev1.dist-info/METADATA +396 -0
- freemyid-0.2.0.dev1.dist-info/RECORD +100 -0
- freemyid-0.2.0.dev1.dist-info/WHEEL +4 -0
- freemyid-0.2.0.dev1.dist-info/entry_points.txt +2 -0
- freemyid-0.2.0.dev1.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: freemyid
|
|
3
|
+
Version: 0.2.0.dev1
|
|
4
|
+
Summary: Local-first personal data broker opt-out tool with scheduled re-checks.
|
|
5
|
+
Project-URL: Homepage, https://freemyid.org
|
|
6
|
+
Project-URL: Source, https://github.com/FreeMyID/freemyid
|
|
7
|
+
Project-URL: Issues, https://github.com/FreeMyID/freemyid/issues
|
|
8
|
+
Author: Free My ID
|
|
9
|
+
License-Expression: AGPL-3.0-or-later
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ccpa,data-broker,gdpr,opt-out,people-search,privacy
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: cryptography>=42.0
|
|
24
|
+
Requires-Dist: desktop-notifier>=6.0
|
|
25
|
+
Requires-Dist: fastapi>=0.111
|
|
26
|
+
Requires-Dist: httpx>=0.27
|
|
27
|
+
Requires-Dist: jinja2>=3.1
|
|
28
|
+
Requires-Dist: jsonschema>=4.22
|
|
29
|
+
Requires-Dist: keyring>=25.0
|
|
30
|
+
Requires-Dist: platformdirs>=4.2
|
|
31
|
+
Requires-Dist: playwright>=1.45
|
|
32
|
+
Requires-Dist: pydantic>=2.7
|
|
33
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
34
|
+
Requires-Dist: pyyaml>=6.0
|
|
35
|
+
Requires-Dist: rapidfuzz>=3.9
|
|
36
|
+
Requires-Dist: rich>=13.7
|
|
37
|
+
Requires-Dist: typer>=0.12
|
|
38
|
+
Requires-Dist: uvicorn>=0.30
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
41
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
42
|
+
Requires-Dist: pillow>=10; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
45
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
46
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
47
|
+
Provides-Extra: package
|
|
48
|
+
Requires-Dist: pyinstaller>=6.8; extra == 'package'
|
|
49
|
+
Requires-Dist: pywebview>=5.0; extra == 'package'
|
|
50
|
+
Provides-Extra: pdf
|
|
51
|
+
Requires-Dist: weasyprint>=62; extra == 'pdf'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# Free My ID
|
|
55
|
+
|
|
56
|
+
**Take your name off the websites that sell it.**
|
|
57
|
+
|
|
58
|
+
Free My ID is a local-first tool that finds your listings on people-search sites (also called data brokers), asks each one to delete your details, and keeps checking so it can ask again when listings come back.
|
|
59
|
+
|
|
60
|
+
No account. No cloud. It never asks for your Social Security number, bank or card details. Free and open source. Uninstall anytime.
|
|
61
|
+
|
|
62
|
+
It runs on your own computer. There is no AI or cloud service at runtime: every step is deterministic Python driven by a broker registry (YAML) and a per-listing state machine. Your personal data stays in an encrypted database on your machine.
|
|
63
|
+
|
|
64
|
+
It works on people-search and data-broker sites only. It does not change what Facebook or Google does with your account.
|
|
65
|
+
|
|
66
|
+
**[Download for Windows](https://freemyid.org/download/)** | **[Website and plain-English guide](https://freemyid.org/)** | **[Version 0.1.0 on GitHub](https://github.com/FreeMyID/freemyid/releases/tag/v0.1.0)**
|
|
67
|
+
|
|
68
|
+
> **Status:** early release (0.1.0) for Windows 10 and 11. It is new, so expect rough edges, and please tell us what confuses you. The installer is not code-signed yet, so Windows shows a blue SmartScreen box the first time; the download page shows what to click. Each release lists a SHA256 checksum so you can confirm your download.
|
|
69
|
+
|
|
70
|
+
Not a developer? You can stop reading here and use the [website](https://freemyid.org/) or the [User Guide](docs/USER_GUIDE.md).
|
|
71
|
+
|
|
72
|
+
**Helping a parent or grandparent?** Install it with them, sit beside them for the six setup screens, and use the website's plain-English guide. Most of the rest of this page is for developers.
|
|
73
|
+
|
|
74
|
+
## About the broker list
|
|
75
|
+
|
|
76
|
+
This public repository includes `brokers/registry.sample.yaml`, a sample list of brokers (CC BY-NC 4.0). The full, maintained list ships with the Windows app. If you run from source, the sample is a starting point, not the same coverage.
|
|
77
|
+
|
|
78
|
+
## What it does, and what it doesn't
|
|
79
|
+
|
|
80
|
+
**It does:**
|
|
81
|
+
|
|
82
|
+
- Search each broker in the registry for each person (a "profile"), scoring candidate listings against names, nicknames, previous names, city/state, age, relatives, phone and email.
|
|
83
|
+
- Submit opt-outs through the broker's own channel (web form, email, postal letter, phone script, or a to-do when an account is required), sending only what the listing already shows.
|
|
84
|
+
- Click email verification links in a dedicated removal mailbox, but only links on that broker's own domains.
|
|
85
|
+
- Escalate requests that get ignored: resubmit, then a state-law follow-up email, then a firmer letter (always held for your approval), then a to-do with the right Attorney General or CPPA complaint links.
|
|
86
|
+
- Re-scan on a schedule and file again when a listing reappears.
|
|
87
|
+
- Keep encrypted evidence: screenshots of every listing found and every confirmation page, plus copies of every email sent.
|
|
88
|
+
|
|
89
|
+
**Honest limits:**
|
|
90
|
+
|
|
91
|
+
- **Free My ID works on people-search sites. It does not change what Facebook or Google does with your account.**
|
|
92
|
+
- **A site's "removed" means removed from that one broker.** It does not mean removed from the internet, from search engines, from public records, or from brokers that aren't in the registry.
|
|
93
|
+
- **Listings come back.** Brokers re-ingest data from their sources, and clone sites copy one another. That is why Free My ID keeps re-checking rather than running once.
|
|
94
|
+
- **Some brokers need you.** CAPTCHAs, ID requests, login walls, postal letters and phone calls are handed to a person. Free My ID never solves or bypasses a CAPTCHA or other bot protection.
|
|
95
|
+
- **The registry goes stale.** Brokers move opt-out pages and change forms. Entries carry a `verification_status` (`verified`, `unverified` or `broken`), and a weekly health check flags breakage, but some entries will be wrong at any given time.
|
|
96
|
+
- **Matching is fuzzy.** Only `CONFIRMED` matches (name plus at least 2 other signals) are submitted automatically. `POSSIBLE` matches (name plus 1 signal) wait for you to confirm.
|
|
97
|
+
- **It's not legal advice.** The letter templates cite state privacy laws, but you are responsible for what you send.
|
|
98
|
+
|
|
99
|
+
## Install
|
|
100
|
+
|
|
101
|
+
**Windows 10 or 11:** The installer is offered on the [download page](https://freemyid.org/download/). It is unsigned for now, so Windows shows a blue SmartScreen box the first time; the download page shows what to click. Linux and macOS builds are not offered yet; run from source instead.
|
|
102
|
+
|
|
103
|
+
**From source (developers):** Requires Python 3.11+ (development uses 3.12). Free My ID drives your installed Chrome or Edge first. It only downloads Playwright's bundled browser if neither is present, and it asks before doing so.
|
|
104
|
+
|
|
105
|
+
### With pipx (command line)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pipx install git+https://github.com/FreeMyID/freemyid.git
|
|
109
|
+
freemyid setup
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## For developers and contributors
|
|
113
|
+
|
|
114
|
+
### Dev environment
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python3.12 -m venv .venv # or: uv venv --python 3.12 .venv
|
|
118
|
+
.venv/bin/pip install -e '.[dev]' # or: uv pip install --python .venv -e '.[dev]'
|
|
119
|
+
.venv/bin/freemyid --help
|
|
120
|
+
.venv/bin/pytest -q
|
|
121
|
+
.venv/bin/ruff check src tests
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Optional extras: `.[pdf]` (WeasyPrint, for PDF reports) and `.[package]` (PyInstaller).
|
|
125
|
+
|
|
126
|
+
For development, point Free My ID at a throwaway data directory and a test key so you never touch your real data or OS keychain:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
export FREEMYID_HOME="$(mktemp -d)"
|
|
130
|
+
export FREEMYID_TEST_KEY=dev-only-not-a-secret
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`FREEMYID_TEST_KEY` is for tests and development only. Never set it on a real install, because it replaces the keychain-held database key with one derived from that string.
|
|
134
|
+
|
|
135
|
+
## Commands
|
|
136
|
+
|
|
137
|
+
Run `freemyid --help` or `freemyid <command> --help` for full options. `-v/--verbose` turns on debug logging (still redacted).
|
|
138
|
+
|
|
139
|
+
| Command | What it does |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `freemyid setup [--no-web] [--prefill FILE]` | Add a person. Opens the local web wizard by default; `--no-web` uses terminal prompts. `--prefill` imports a family bundle. |
|
|
142
|
+
| `freemyid dashboard` | Open the local dashboard: status, evidence, to-dos, outbox approvals. |
|
|
143
|
+
| `freemyid scan [-p PROFILE] [-b BROKER ...]` | Search brokers for listings. Never submits anything. |
|
|
144
|
+
| `freemyid run --dry-run [-p ...] [-b ...]` | Full pipeline, but submits nothing. Run this first. |
|
|
145
|
+
| `freemyid run [-p ...] [-b ...]` | Scan, submit opt-outs, check verifications, escalate ignored requests. Refuses to run until real submissions are enabled. |
|
|
146
|
+
| `freemyid status [-p PROFILE]` | Per-broker state, last update, next check and escalation level for each person. |
|
|
147
|
+
| `freemyid report [-p PROFILE] [--format html\|pdf]` | Export a report to the artifacts folder. |
|
|
148
|
+
| `freemyid profile add [--prefill FILE]` | Same as `setup`. |
|
|
149
|
+
| `freemyid profile list` | List people, consent basis and whether a mailbox is configured. |
|
|
150
|
+
| `freemyid profile edit PROFILE` | Edit a person (terminal flow). |
|
|
151
|
+
| `freemyid profile remove PROFILE [--yes]` | Delete a person, all their evidence, and their mailbox password from the keychain. |
|
|
152
|
+
| `freemyid profile export PROFILE DEST` | Write a passphrase-protected `.freemyid-profile` bundle for a family member's install. |
|
|
153
|
+
| `freemyid schedule install\|remove\|status` | Manage the background re-check job. |
|
|
154
|
+
| `freemyid registry show` | List brokers: method, verification, CAPTCHA, `covered_by`. |
|
|
155
|
+
| `freemyid registry validate [PATH]` | Validate a registry file against the model and JSON Schema. |
|
|
156
|
+
| `freemyid registry update` | Fetch and validate a new registry from `registry_url`. |
|
|
157
|
+
| `freemyid todo list [-p PROFILE]` | Items that need a person. |
|
|
158
|
+
| `freemyid todo done ID` | Mark a to-do done (an ID prefix is enough). |
|
|
159
|
+
| `freemyid outbox list` | Emails waiting for your approval. |
|
|
160
|
+
| `freemyid outbox approve ID` | Approve and send one queued email. |
|
|
161
|
+
| `freemyid config set KEY VALUE` | Change a setting in `config.yaml`. Protected keys are rejected. |
|
|
162
|
+
| `freemyid config enable-submissions` | One-time explicit opt-in to real submissions. |
|
|
163
|
+
|
|
164
|
+
`PROFILE` matches an ID prefix or the profile's label (e.g. `Mom`). Profiles that fail the consent gate are skipped with a message.
|
|
165
|
+
|
|
166
|
+
### Safety gates, in order
|
|
167
|
+
|
|
168
|
+
1. **Consent gate** (`cli.consent_ok`, re-checked in the runner). Every profile needs an attesting name. A minor needs `guardian_of_minor` consent. An `authorized_agent` needs a signed authorization attached.
|
|
169
|
+
2. **Dry run first.** `freemyid run` without `--dry-run` exits until `freemyid config enable-submissions` has been confirmed interactively. `real_submissions_enabled` can't be set with `config set`.
|
|
170
|
+
3. **Email approval.** With `email_mode: approve_each` (the default), every outgoing email waits in the outbox. Legal demands always wait, even in `auto` mode.
|
|
171
|
+
4. **Match gate.** Only `CONFIRMED` listings are auto-submitted.
|
|
172
|
+
5. **Permission flags.** ID upload, account creation and phone disclosure are off by default per profile. When a broker needs one of them, you get a `permission_needed` to-do instead.
|
|
173
|
+
|
|
174
|
+
## Architecture
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
src/freemyid/
|
|
178
|
+
cli.py Typer CLI (thin; all logic lives in modules)
|
|
179
|
+
models.py Pydantic models: Profile, Broker, Registry, StateLaw, Listing, Request, ...
|
|
180
|
+
state.py Pure state machine + escalation ladder + re-check timing
|
|
181
|
+
interfaces.py Contracts between modules (Submitter, Mailer, HumanHandoff, Notifier, entry points)
|
|
182
|
+
registry.py Load/validate/update brokers/registry.yaml and state_laws.yaml
|
|
183
|
+
netpolicy.py Network allowlist; every request goes through NetPolicy.is_allowed
|
|
184
|
+
crypto.py AES-256-GCM; key in the OS keychain; passphrase export (scrypt)
|
|
185
|
+
secrets.py Mailbox passwords in the OS keychain
|
|
186
|
+
storage/db.py Encrypted SQLite store (Store)
|
|
187
|
+
logging_setup.py PII-redacting log filter
|
|
188
|
+
config.py, paths.py Non-PII settings and file locations
|
|
189
|
+
engine/ runner, matcher, browser (Playwright), mail, handoff, outbox, letters, plugins
|
|
190
|
+
submitters/ web_form, email, postal, phone, account_required + per-broker classes
|
|
191
|
+
templates/ jinja2 letters: generic, ccpa, state_privacy, follow_up, legal_demand, postal_letter, phone_script
|
|
192
|
+
wizard/ FastAPI wizard + dashboard (localhost only), flows, report
|
|
193
|
+
scheduler/ systemd user timer / cron, launchd, schtasks
|
|
194
|
+
notify.py Desktop notifications (PII-free)
|
|
195
|
+
brokers/ registry.yaml, state_laws.yaml, SOURCES.md
|
|
196
|
+
schema/ JSON Schemas generated from models (tools/gen_schema.py)
|
|
197
|
+
tools/ gen_schema.py, registry_health.py
|
|
198
|
+
tests/ pytest suite
|
|
199
|
+
packaging/ PyInstaller spec, installers
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Module ownership
|
|
203
|
+
|
|
204
|
+
Each area has one owner. Others send change requests rather than editing it. Agent definitions are in `.claude/agents/`.
|
|
205
|
+
|
|
206
|
+
| Owner | Files |
|
|
207
|
+
|---|---|
|
|
208
|
+
| orchestrator | `models.py`, `state.py`, `interfaces.py`, `registry.py`, `netpolicy.py`, `crypto.py`, `secrets.py`, `logging_setup.py`, `config.py`, `paths.py`, `cli.py`, `storage/`, `schema/`, `pyproject.toml`, `PROGRESS.md` |
|
|
209
|
+
| broker-researcher | `brokers/registry.yaml`, `brokers/state_laws.yaml`, `brokers/SOURCES.md` |
|
|
210
|
+
| automation-engineer | `engine/`, `submitters/`, `templates/` |
|
|
211
|
+
| wizard-packager | `wizard/`, `scheduler/`, `notify.py`, `packaging/`, `.github/workflows/`, `FAMILY_QUICK_START.md` |
|
|
212
|
+
| qa-security | `tests/`, `tools/registry_health.py`, `docs/SECURITY_REVIEW.md` |
|
|
213
|
+
|
|
214
|
+
### How a run works
|
|
215
|
+
|
|
216
|
+
For each consenting profile, the runner goes through enabled brokers by `priority` (highest first), one broker at a time:
|
|
217
|
+
|
|
218
|
+
1. Load the `(profile, broker)` status. Scheduled runs skip brokers whose `next_check_at` is still in the future.
|
|
219
|
+
2. **Search** through the broker's submitter, score the listings, then apply `state.after_scan` and `state.transition`.
|
|
220
|
+
3. **Submit** if the state is `found` or `reappeared`, the listing is `CONFIRMED`, and this isn't a dry run. When a broker has `covered_by` set, the opt-out goes to the parent broker and this site is only re-scanned.
|
|
221
|
+
4. **Verify** by polling IMAP for confirmation links on the broker's `confirmation_domains`.
|
|
222
|
+
5. **Escalate** if the request has been ignored. Each rung becomes due `typical_processing_days + 15` days after the last submission or escalation: resubmit → follow-up email → legal demand (always approved by you) → complaint to-do. A removal resets the ladder.
|
|
223
|
+
6. **Schedule the next check:** every 3–14 days while a request is pending, every `recheck_interval_days` once removed or not found, and at least weekly while something needs attention.
|
|
224
|
+
|
|
225
|
+
A failure at one broker never stops the run. CAPTCHAs and other challenges go to `HumanHandoff`. An interactive run opens a visible browser window and waits for you. A scheduled run creates a to-do and sends a notification without blocking.
|
|
226
|
+
|
|
227
|
+
Listing states: `unknown`, `not_found`, `found`, `submitted`, `awaiting_verification`, `confirmed`, `removed`, `reappeared`, `needs_human`, `error`. Allowed transitions are in `state.TRANSITIONS`.
|
|
228
|
+
|
|
229
|
+
## Security and privacy model
|
|
230
|
+
|
|
231
|
+
**What's stored, and where.** Everything lives under `~/.freemyid/` (`%APPDATA%\Free My ID` on Windows), or under `FREEMYID_HOME` if set. The directory is created with mode `0700`.
|
|
232
|
+
|
|
233
|
+
| Path | Contents | Protection |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| `freemyid.db` | Profiles, statuses, listings, requests, to-dos, append-only event log | SQLite. Only ids, enums and timestamps are in clear text. Every model is an AES-256-GCM blob with AAD bound to (table, id). |
|
|
236
|
+
| `evidence/` | Screenshots, HTML, sent/received emails | Encrypted files. The dashboard decrypts them in memory only. |
|
|
237
|
+
| `artifacts/` | Generated letters, call scripts, reports | |
|
|
238
|
+
| `logs/` | Rotating logs | PII-redacted: known profile values plus email/phone patterns. Code logs ids and enums only. |
|
|
239
|
+
| `config.yaml` | Non-PII settings | Mode `0600` |
|
|
240
|
+
| `registry.yaml` | Cached registry update | Public data |
|
|
241
|
+
|
|
242
|
+
**Keys and passwords.** The database key is a random 256-bit key stored in the OS keychain (service `freemyid`) through `keyring`. Mailbox passwords also live only in the keychain (service `freemyid-mailbox`), never in the database or config. If the keychain is locked or unavailable (for example, in a headless session), Free My ID raises `KeyUnavailable` and surfaces it as needing a person; it does not fall back to plaintext.
|
|
243
|
+
|
|
244
|
+
**Family bundles.** `profile export` encrypts a profile with a passphrase (scrypt, then AES-GCM). Share the passphrase by phone, not in the same channel as the file. The family member reviews the imported data and gives their own consent.
|
|
245
|
+
|
|
246
|
+
**Network allowlist.** `NetPolicy` permits only:
|
|
247
|
+
|
|
248
|
+
- domains and confirmation domains of brokers in the registry
|
|
249
|
+
- the profile's own IMAP/SMTP hosts
|
|
250
|
+
- the host of `registry_url`
|
|
251
|
+
- hosts you add to `extra_allowed_hosts`
|
|
252
|
+
- `localhost` and `127.0.0.1`
|
|
253
|
+
|
|
254
|
+
Browser requests go through a Playwright route that aborts anything off the list. httpx, SMTP and IMAP calls check it first.
|
|
255
|
+
|
|
256
|
+
**No telemetry.** No analytics, crash reporting or update pings. The wizard and dashboard load no external assets (no CDNs or web fonts).
|
|
257
|
+
|
|
258
|
+
**Local web UI.** The wizard and dashboard bind to `127.0.0.1` only, use a random per-launch session token, and have CSRF protection on every POST.
|
|
259
|
+
|
|
260
|
+
**Data minimization.** A broker receives only the fields its listing already shows (`Listing.shown_fields`) plus the removal mailbox address. DOB, relatives and personal emails are used only for matching and are never sent. Phone numbers, ID documents and account creation each need their own permission flag.
|
|
261
|
+
|
|
262
|
+
**Registry updates** come only from your configured `registry_url`. They are validated against the Pydantic model and the JSON Schema, can be pinned to a `.sha256` file, and are swapped in atomically. An update that fails validation never replaces the working copy.
|
|
263
|
+
|
|
264
|
+
Security review findings are in `docs/SECURITY_REVIEW.md` (owned by qa-security).
|
|
265
|
+
|
|
266
|
+
## Adding a broker
|
|
267
|
+
|
|
268
|
+
Most brokers need only a YAML entry. Python is for sites a declarative form can't handle.
|
|
269
|
+
|
|
270
|
+
### 1. Registry entry (`brokers/registry.yaml`)
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
- id: examplepeople # ^[a-z0-9_]+$
|
|
274
|
+
name: ExamplePeople
|
|
275
|
+
url: https://www.examplepeople.test/
|
|
276
|
+
category: people_search # people_search | background_check | data_broker | risk_data | family_tree | phone_lookup | other
|
|
277
|
+
domains: [examplepeople.test] # every host the opt-out flow touches (feeds the allowlist)
|
|
278
|
+
search_url_pattern: "https://www.examplepeople.test/name/{first}-{last}/{state}" # only if you've seen the real shape
|
|
279
|
+
opt_out_url: https://www.examplepeople.test/optout
|
|
280
|
+
method: web_form # web_form | email | postal | phone | account_required
|
|
281
|
+
alt_methods: [email]
|
|
282
|
+
opt_out_email: privacy@examplepeople.test
|
|
283
|
+
required_fields: [full_name, listing_url, email]
|
|
284
|
+
needs_email_verification: true
|
|
285
|
+
confirmation_domains: [examplepeople.test]
|
|
286
|
+
has_captcha: false
|
|
287
|
+
needs_id_upload: false
|
|
288
|
+
typical_processing_days: 14
|
|
289
|
+
recheck_interval_days: 30
|
|
290
|
+
feeds_from: [] # broker ids this site copies from
|
|
291
|
+
covered_by: null # broker id whose opt-out also covers this site
|
|
292
|
+
steps:
|
|
293
|
+
- Search for your listing and copy its URL.
|
|
294
|
+
- Paste it into the opt-out form and enter the removal email.
|
|
295
|
+
- Click the link in the confirmation email.
|
|
296
|
+
search: # selectors only from real or saved HTML
|
|
297
|
+
result_selector: ".result-card"
|
|
298
|
+
name_selector: ".result-card h2"
|
|
299
|
+
age_selector: ".result-card .age"
|
|
300
|
+
location_selector: ".result-card .locations li"
|
|
301
|
+
relatives_selector: ".result-card .relatives li"
|
|
302
|
+
link_selector: ".result-card a.profile-link"
|
|
303
|
+
no_results_text: "No results found"
|
|
304
|
+
form:
|
|
305
|
+
- {action: goto, url: "https://www.examplepeople.test/optout"}
|
|
306
|
+
- {action: fill, selector: "#listing-url", value: "{{ listing.url }}"}
|
|
307
|
+
- {action: fill, selector: "#email", value: "{{ mailbox.address }}"}
|
|
308
|
+
- {action: screenshot}
|
|
309
|
+
- {action: submit, selector: "button[type=submit]"}
|
|
310
|
+
- {action: expect_text, value: "check your email"}
|
|
311
|
+
source_urls: [https://www.examplepeople.test/privacy]
|
|
312
|
+
verification_status: unverified # verified only after loading the live page yourself
|
|
313
|
+
verification_notes: "Not yet checked live."
|
|
314
|
+
priority: 50 # 0-100, higher runs first
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Form step actions: `goto`, `fill`, `select`, `check`, `click`, `wait_for`, `screenshot`, `submit`, `expect_text`, `human`. A `value` is a jinja2 template rendered with `profile`, `listing`, `mailbox` and `broker`. Only reference fields in `listing.shown_fields`, and use the mailbox address, never a personal email. A `human` step hands the page to a person.
|
|
318
|
+
|
|
319
|
+
Rules for registry entries:
|
|
320
|
+
|
|
321
|
+
- Never invent a URL, email, address or phone number. Cite `source_urls`.
|
|
322
|
+
- Set `verified` only after loading the live page.
|
|
323
|
+
- Leave `search` and `form` selectors empty until you have real or saved HTML.
|
|
324
|
+
|
|
325
|
+
Then validate the entry and regenerate the schemas if you changed models:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
.venv/bin/freemyid registry validate brokers/registry.yaml
|
|
329
|
+
.venv/bin/python tools/gen_schema.py # only after models.py changes
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 2. Optional custom submitter
|
|
333
|
+
|
|
334
|
+
If the declarative form isn't enough, subclass `freemyid.interfaces.Submitter`:
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from freemyid.interfaces import RunContext, Submitter
|
|
338
|
+
from freemyid.models import Listing, SubmitOutcome
|
|
339
|
+
|
|
340
|
+
class ExamplePeopleSubmitter(Submitter):
|
|
341
|
+
method = "web_form"
|
|
342
|
+
|
|
343
|
+
async def search(self, ctx: RunContext) -> list[Listing]:
|
|
344
|
+
... # never submits; screenshot each listing found via ctx.store.save_evidence
|
|
345
|
+
|
|
346
|
+
async def submit(self, ctx: RunContext, listings: list[Listing]) -> SubmitOutcome:
|
|
347
|
+
... # never called in dry_run; send only listing.shown_fields + mailbox
|
|
348
|
+
|
|
349
|
+
async def verify(self, ctx: RunContext) -> SubmitOutcome | None:
|
|
350
|
+
... # optional
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The engine looks a submitter up in this order:
|
|
354
|
+
|
|
355
|
+
1. `submitter: "module:Class"` in the registry entry
|
|
356
|
+
2. an entry point in the `freemyid.submitters` group, keyed by broker id (for third-party packages)
|
|
357
|
+
3. a `freemyid.submitters.<broker_id>` module containing a class named `Submitter`
|
|
358
|
+
4. the generic submitter for `broker.method`
|
|
359
|
+
|
|
360
|
+
Write the search parser against saved HTML in `tests/fixtures/<broker_id>/`. If you couldn't test it against the live site, mark it `UNTESTED_AGAINST_LIVE`. Every submitter must follow the rules in the `Submitter` docstring:
|
|
361
|
+
|
|
362
|
+
- no CAPTCHA bypass
|
|
363
|
+
- data minimization
|
|
364
|
+
- `CONFIRMED`-only auto-submit
|
|
365
|
+
- screenshot evidence
|
|
366
|
+
- page text treated as data
|
|
367
|
+
|
|
368
|
+
## Contributing
|
|
369
|
+
|
|
370
|
+
### Ground rules
|
|
371
|
+
|
|
372
|
+
These apply to people and agents alike:
|
|
373
|
+
|
|
374
|
+
- **No AI or API keys at runtime.** Everything stays deterministic and local.
|
|
375
|
+
- **No PII anywhere it could leak:** logs, events, exceptions, fixtures, tests or commits. Use fake people only, e.g. "Jane Q Testperson" of Springfield, IL.
|
|
376
|
+
- **No network calls outside `NetPolicy`.** No telemetry.
|
|
377
|
+
- **Never bypass a CAPTCHA.** No solver services and no stealth plugins.
|
|
378
|
+
- **During development:** don't send real email, submit real opt-outs, or install scheduler jobs or browsers system-wide. Use `--dry-run`, fixtures and fakes.
|
|
379
|
+
- **Before a PR:** `.venv/bin/pytest -q` and `.venv/bin/ruff check src tests` must pass. Run `freemyid registry validate` if you touched the registry.
|
|
380
|
+
|
|
381
|
+
### Agent workflow
|
|
382
|
+
|
|
383
|
+
Free My ID is built by an orchestrator session plus four specialist Claude Code agents (`.claude/agents/`). AI is used only to *build* Free My ID, never inside it at runtime.
|
|
384
|
+
|
|
385
|
+
1. Every agent reads `.claude/agents/_shared-rules.md` first.
|
|
386
|
+
2. Agents edit only files they own. To change anything else, an agent files a **CHANGE REQUEST** (file, exact change, reason) in its report, and the orchestrator applies it.
|
|
387
|
+
3. The contracts (`models.py`, `interfaces.py`, `state.py`, `storage/`, ...) are read-only for everyone except the orchestrator.
|
|
388
|
+
4. Agents draft tests in `_test_drafts/` folders, and qa-security adopts them into `tests/`.
|
|
389
|
+
5. qa-security reviews every change and returns **PASS** or **BLOCK**. Anything that weakens a safety gate is a BLOCK.
|
|
390
|
+
6. A finished task's report lists the files changed, how to test them, the test results, open questions and change requests.
|
|
391
|
+
7. The orchestrator integrates, updates `PROGRESS.md` and signs off. These need explicit human approval: system-wide installs, the first real submission, any real email, and any `git push`.
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
- **Code:** GNU AGPL v3 or later (see [LICENSE](LICENSE)).
|
|
396
|
+
- **Broker data in `brokers/`** (`registry.sample.yaml`, `state_laws.yaml`): CC BY-NC 4.0, non-commercial use with credit. See [brokers/LICENSE-DATA.md](brokers/LICENSE-DATA.md).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
freemyid/__init__.py,sha256=Ppe8t4_LIQMqliAibmUZ8Vvf4oqQbxMPxSQ6TVjyYnc,83
|
|
2
|
+
freemyid/cli.py,sha256=f7KKQQM2XK-28Yh1AAY7cDC-JXnFg9bRrLfRYc921eo,28467
|
|
3
|
+
freemyid/cli_json.py,sha256=4jhspczCmERDmvwmGPy4vpz1hQFhC-_v2CLps3Fmmgs,7243
|
|
4
|
+
freemyid/config.py,sha256=tqkOr24Zs9YcNkc9V5Use_hRTdcgJ3Pj4_HOrd7HyI8,1627
|
|
5
|
+
freemyid/consent.py,sha256=VyFtZkMazExzFitmrU7PCIHGtJqxcXKZDz8Ios9Vlow,3495
|
|
6
|
+
freemyid/crypto.py,sha256=eGquFk8tvTNxUvjOAkwII_bJQOy_D-Wugd3q9-rUXkQ,7703
|
|
7
|
+
freemyid/doctor.py,sha256=uRaYMUHYrAd5GojNTZhngaiyGUtIPVpjjeYRS5IfGYM,16049
|
|
8
|
+
freemyid/interfaces.py,sha256=n3wo22KxEWEfjruf6WUU_30JLEWU-z7jCfy7lHore7I,7605
|
|
9
|
+
freemyid/logging_setup.py,sha256=ovcuLBykf4Tv4DB1yHaUlRVShVWgY_J8lGXsbsW8og0,6224
|
|
10
|
+
freemyid/models.py,sha256=ZaTNIPc1DpEKNuuoOJd28rK0NlezFwIftDUErqcdXpw,26825
|
|
11
|
+
freemyid/netpolicy.py,sha256=9i03uQIPXNwjG0djqBVlpMOtML-HaOUcXjuo_OP0g1k,5885
|
|
12
|
+
freemyid/notify.py,sha256=VxptWJWZ0FxF36GPJ6Z7n6Iby-AtKppCV9bUgrjt05c,2060
|
|
13
|
+
freemyid/paths.py,sha256=lCG2ik7aJpdfE66xSrlztGJ24Qx7M_dANHfFv9nsb-g,2751
|
|
14
|
+
freemyid/registry.py,sha256=MxLVQheSMbQMTxv-MMXVSiPhZlEP4a6LPZAi9gAgBh8,11024
|
|
15
|
+
freemyid/secret_backends.py,sha256=82aBW_0Y-Lb5UmkpV0GbYIXhfRkxoaMKxmXn2UvAD1Y,22135
|
|
16
|
+
freemyid/secrets.py,sha256=C3FolVTpt6BkBh6Asx3OjI_OnRkEEGHKSnAFwWwX9Uo,965
|
|
17
|
+
freemyid/setup_config.py,sha256=BumtvdaJWxeqreAcqGZVOE4KR8sUG9hRBAUrpq6SRXA,13792
|
|
18
|
+
freemyid/state.py,sha256=vYCTZcGBJTf96qbqtlSPdxnz0OQVZ99_vJdTR0DQEno,6262
|
|
19
|
+
freemyid/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
freemyid/engine/browser.py,sha256=QIJhGsWYxOBWoUrD4-T-yn1OiSABKHKdXt2Beb91-pw,19270
|
|
21
|
+
freemyid/engine/fill_for_me.py,sha256=Sn8EPktoWsxQjoaJl1kijrCkamkKr21WBaRi-09cXls,28118
|
|
22
|
+
freemyid/engine/filler.py,sha256=sp113IWiB_fJ66w3i3sPQG081H0Z2nFeu3_xGpvsOik,23352
|
|
23
|
+
freemyid/engine/handoff.py,sha256=Wc1Z94dAl5L1iVuFzetx-kTvzQeXY7vm4Y2UekV9eXE,10386
|
|
24
|
+
freemyid/engine/letters.py,sha256=3_-EZxCnVSIxUY2JmFW1QFlTmBqDg5CGMAwjaO2xdEU,5805
|
|
25
|
+
freemyid/engine/listing_finder.py,sha256=29HyECv1peMyunh7uirPjVMRKB0At2EyBXj3VvW-8YU,23900
|
|
26
|
+
freemyid/engine/mail.py,sha256=aOedaFlsvYkoZ6xkY-s8R75eL4N5HSs-R8DE6DZJnvk,17155
|
|
27
|
+
freemyid/engine/mail_tls.py,sha256=Qs8nTxNzTnEm6nYTwuleTLdseTzk663U8_lIO8DmatQ,9379
|
|
28
|
+
freemyid/engine/matcher.py,sha256=LITOQ3YW-eqDtLylB5SkveLvHjtDezGb-nk-VbOxQJM,7938
|
|
29
|
+
freemyid/engine/outbox.py,sha256=dVgGlUipHSMtXiRG3heG4vjCrkl-M0ZvZiJ3AKDTSZY,19311
|
|
30
|
+
freemyid/engine/plugins.py,sha256=NUzMHadgnCu-2MOWJjK8lO9H_gLTsLJK5iFaYCLgf44,2521
|
|
31
|
+
freemyid/engine/runner.py,sha256=RVvEHdy_GILvkrRW-xSEWZVekdVxA7Yn8HiMlYoLnIg,39449
|
|
32
|
+
freemyid/engine/tiers.py,sha256=xRR-93O5OLNTrtt38ARlI_7ddsFYtSEV0O5I9M1cU80,4191
|
|
33
|
+
freemyid/engine/_dev/__init__.py,sha256=RfheX78Wf-aOg1DLdOfYOacvjMbZqVNqtdu5j2JkSUs,3625
|
|
34
|
+
freemyid/engine/_dev/devrun.py,sha256=ekDxjTn5ORrRGALeQcQVoMd_r6IrTlj4DSZkA-MN2S4,4516
|
|
35
|
+
freemyid/engine/_dev/registry.yaml,sha256=t4jxXx1Mpee_bdoB8NCiEZ4PMLPgwv5E7KhvHb--6Es,4136
|
|
36
|
+
freemyid/engine/_dev/inbox/devauto.eml,sha256=L0U-DwqE2MI4Qj5W7U0BIsAfkExulKNyD9SvWDkq4xA,311
|
|
37
|
+
freemyid/engine/_dev/site/devauto.example/confirm.html,sha256=yofiBZ6OqBvSNPKWUFMbODX6L8y_E7xDHjV3EH3aGuQ,93
|
|
38
|
+
freemyid/engine/_dev/site/devauto.example/optout.html,sha256=cuCBtO9liSARrd_XJNVrslMnaPzc0MTlTLbj8r9s5EA,254
|
|
39
|
+
freemyid/engine/_dev/site/devauto.example/name/jane-testperson/il.html,sha256=s9MM_hYV20QKkx99kIbnmafaRszEPGNw10Ck9vHr3kE,696
|
|
40
|
+
freemyid/engine/_dev/site/devauto.example/name/jane-testperson/nc.html,sha256=ZtVMHuOLhcs80Dg0AbGvbv0F-iwhoiBpxRvGJoDGFBE,161
|
|
41
|
+
freemyid/engine/_dev/site/devauto.example/name/jane-testperson/oh.html,sha256=Vbi6ES_aYkRGdF8I6gl2CCGBPIRARBn7WusOctQV41o,185
|
|
42
|
+
freemyid/engine/_dev/site/devauto.example/optout/done.html,sha256=-zlDuYpaGV5pSZDAFnM6hBapinhKjYUmsf8ZMd8IP-U,107
|
|
43
|
+
freemyid/engine/_dev/site/devblocked.example/optout.html,sha256=2THCF1Dv7fZQ0vjQ6Cg3dUbaKxM9WbuYeYKJ1ct6dWw,194
|
|
44
|
+
freemyid/engine/_dev/site/devblocked.example/name/jane-testperson/il.html,sha256=2THCF1Dv7fZQ0vjQ6Cg3dUbaKxM9WbuYeYKJ1ct6dWw,194
|
|
45
|
+
freemyid/engine/_dev/site/devfill.example/ajax.html,sha256=4zCYdgRewpE065oX37vbrrlvc8FA0MEHlzL6lHjAMh8,918
|
|
46
|
+
freemyid/engine/_dev/site/devfill.example/aria.html,sha256=lc5GCT9wmhRpVG1TQKDwxtohGUlVwEkpMmWqhEFczYA,719
|
|
47
|
+
freemyid/engine/_dev/site/devfill.example/consent.html,sha256=Wf_dqT9w3JZX7VISHukWpZ0g2LJv-80mrJ3MV4iyT9w,1230
|
|
48
|
+
freemyid/engine/_dev/site/devfill.example/done.html,sha256=_MLjuuLa-T2EabsKjqqevr_uK8VPVfTmc-qUl2ZPPfI,180
|
|
49
|
+
freemyid/engine/_dev/site/devfill.example/empty.html,sha256=_J6m2AJX4uH1Cl1xF_QEcjwd6Wbo4JRyPKXMIoKC11U,94
|
|
50
|
+
freemyid/engine/_dev/site/devfill.example/fullname.html,sha256=ArRNbzgnp98lhU9aPxfMtjMVXXLvUhJkkmuNgyTGY3Y,626
|
|
51
|
+
freemyid/engine/_dev/site/devfill.example/labels.html,sha256=W0-bUdGAEBGGbcE03Mr4auYfFpIBdpyofW1RChpsGng,1798
|
|
52
|
+
freemyid/engine/_dev/site/devfill.example/late.html,sha256=vJzqs8Nam9AKF9r6IEj7dtsoVa3XlJfLUzlh_LaWSrg,1004
|
|
53
|
+
freemyid/engine/_dev/site/devfind.example/hostile.html,sha256=MgjELACJTv3uhiMnLvo5UYGbTudZxCbk_DzokOXG9gk,499
|
|
54
|
+
freemyid/engine/_dev/site/devfind.example/results.html,sha256=BAYELt2iIrj8kQbBfEvhtVdyvHOVKRjB2cCW4R65qWA,592
|
|
55
|
+
freemyid/engine/_dev/site/devfind.example/search.html,sha256=ervqWxLHDmmObkcvjlF5moaEKAYKFsDVyxDxYFuxyQw,713
|
|
56
|
+
freemyid/engine/_dev/site/devfind.example/p/jane-testperson-shelbyville-il.html,sha256=sX3UwzAGrXVAjfGRAw64pgNCWXGXmVvrCfaDaU46n-o,311
|
|
57
|
+
freemyid/engine/_dev/site/devfind.example/p/jane-testperson-springfield-il.html,sha256=udiWkkWG5WK2uIj_7JlApS3halkAppMRj43K5Z1RTtM,337
|
|
58
|
+
freemyid/engine/_dev/site/devweb.example/remove.html,sha256=FhsaeShj1BKLodVQmSKVwbW6XL50LPZiG5K9v8PmynI,97
|
|
59
|
+
freemyid/scheduler/__init__.py,sha256=KzqJlVLabQIt0KVy0BaDJCwRdlCn9NbTKHSc9ExbEJ4,8272
|
|
60
|
+
freemyid/scheduler/render.py,sha256=D7H33a3HazxWYb_KKjpiT7LdKxfZRD0hk3panZ0mjz8,4749
|
|
61
|
+
freemyid/storage/__init__.py,sha256=2RZhK4dUCDWh6_1BrRLkeXMC1PLebQQQZNMVJU4EfhE,43
|
|
62
|
+
freemyid/storage/db.py,sha256=Bjy3rqKKggc7SmRnw6KeJFneYXRE9Y7CXH1mM3Ldh1Y,17807
|
|
63
|
+
freemyid/submitters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
+
freemyid/submitters/assisted.py,sha256=vg8yOSADAJeBrkRAtdp5bVfKDFhg97n4s7eE9rkYqys,8217
|
|
65
|
+
freemyid/submitters/base.py,sha256=_iaZy4Wilx9YH7hiezkW5uTxgCgyntx51hdGt-jZF-0,5402
|
|
66
|
+
freemyid/submitters/email.py,sha256=pOrmoVh11A3ZC6I6RyabGqEvX-Tzzom3m3Yr9BlaaSQ,2167
|
|
67
|
+
freemyid/submitters/web_form.py,sha256=WyqLUJ0eL8fR_lZSm_6a-NLax_QOEO_tvUvT2BqNT6k,11993
|
|
68
|
+
freemyid/templates/_identity.txt.j2,sha256=riDwwfaODV_a2cvjQw0FfJtX3cSxhE6UhByOb_2lbIk,606
|
|
69
|
+
freemyid/templates/_signature.txt.j2,sha256=IcPA0R4o18eTZRUST4HQ3Nf4Buey3h9d3X2-tb6T9QU,212
|
|
70
|
+
freemyid/templates/ccpa.txt.j2,sha256=tpkLCBTDs9kDKc53Kilzz3hP3xIs4CJw5V2xvY-uFFk,802
|
|
71
|
+
freemyid/templates/follow_up.txt.j2,sha256=OVG9Sti1jMQM7xnoTnjolkMp9j22diNlNVdbwhrvc-A,748
|
|
72
|
+
freemyid/templates/generic_removal.txt.j2,sha256=0d9OPaPyCZHr0PHQFEvEE_w_FDRgflBcaMfUX_TkypE,415
|
|
73
|
+
freemyid/templates/legal_demand.txt.j2,sha256=BDgn-Wa7C48a3A0ErylkNzpbxQRYfgGnSb3V3eo9eoQ,932
|
|
74
|
+
freemyid/templates/phone_script.txt.j2,sha256=KNbGx5aYwtjcB7dOjwYXdMkfGcjm_5nNzVCaVEojxRA,1163
|
|
75
|
+
freemyid/templates/postal_letter.txt.j2,sha256=imyFu-vRV3ryA2z-tNmGC3IQcA1_x65sjBAWg2bCH9U,550
|
|
76
|
+
freemyid/templates/state_privacy.txt.j2,sha256=KVT4rPndrjo7ShdB5k_QgK0ugNRVcRPWK1FKoGPUsMU,749
|
|
77
|
+
freemyid/wizard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
|
+
freemyid/wizard/dashboard.py,sha256=GrJv6iIHwU2ha59NZYKlnqvP043XFboTGh0X7mqUmf4,22532
|
|
79
|
+
freemyid/wizard/desktop.py,sha256=gHgXb-ninc2_JrEx3bfhz_nkQ5YfUaaoKn0KSdwaYso,6830
|
|
80
|
+
freemyid/wizard/draft.py,sha256=XRbTUBVrBQScE5jZZ6hcaxwpe7nNmTjKzi7OchE4ujE,27962
|
|
81
|
+
freemyid/wizard/firstrun.py,sha256=3dvsZl3jvAxUSLb_8TKfKBDldr1JYz7FNfClL4sAbi0,1526
|
|
82
|
+
freemyid/wizard/flows.py,sha256=c4UAUEIqwhuk23093RvrfHyG3ma5H1MnFmBFHTBlzkk,11213
|
|
83
|
+
freemyid/wizard/report.py,sha256=DA3PCO27gsn0N6iNBosyEPXD4MdmCNLFq1xFKrs857Y,4134
|
|
84
|
+
freemyid/wizard/server.py,sha256=vN7rtv8qi0LTy94Vix1qcx4opBBhBXWtkafv0kKLugQ,36482
|
|
85
|
+
freemyid/wizard/ui.py,sha256=15lYBdox7DOMiA3bsawYVCM_x59wg1uYc3_Xeh9d3RI,20060
|
|
86
|
+
freemyid/wizard/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
87
|
+
freemyid/wizard/static/app.js,sha256=4ReYuHbyhzkL-s764Er7u2HJyzkKFQI1i9LWcmvutAw,4087
|
|
88
|
+
freemyid/wizard/static/freemyid.png,sha256=0x73Q2Uw2uPsEvsXDE937REMypYh9vKrojXVPqBZbnk,11143
|
|
89
|
+
freemyid/wizard/static/style.css,sha256=SvmbNubWRHf7EIrwo_PjbMekNtzti_Sj-33KzcpqJQ0,5378
|
|
90
|
+
freemyid/_data/schema/registry.schema.json,sha256=h5F79tfoG9bKfKwpqG9Sj5kTDrvT1TONUxPZaAV_lgA,15689
|
|
91
|
+
freemyid/_data/schema/setup-config.schema.json,sha256=FnAb-dam2BdEXOajyL_pqoLdR0zKJDFcsSo2_X1f0xI,12669
|
|
92
|
+
freemyid/_data/schema/state_laws.schema.json,sha256=MXQGQcKtpVtVlZWjWF9alPunOM9ez5llH91GwPB020E,4765
|
|
93
|
+
freemyid/_data/brokers/LICENSE-DATA.md,sha256=rqrXSz15lGW-jvosfZdIWGHYh3xb4j05ITcpDt4pf9g,1404
|
|
94
|
+
freemyid/_data/brokers/registry.sample.yaml,sha256=60ret4HlbsX4lYSV46FAftfhj2bYDg6MOizM3gxpabM,40252
|
|
95
|
+
freemyid/_data/brokers/state_laws.yaml,sha256=mybu-szvnXNI9tp8HU8_OzIQLchyPisca7mDSOVSf6I,27862
|
|
96
|
+
freemyid-0.2.0.dev1.dist-info/METADATA,sha256=9nvRP77jTFp9Hfom9dzmmrRki2mn6oRSt2a2wqFqSGQ,24409
|
|
97
|
+
freemyid-0.2.0.dev1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
98
|
+
freemyid-0.2.0.dev1.dist-info/entry_points.txt,sha256=tSnFk2bCUOEK3LY5ZcYPjtwu5aHiP3yncdFdVflh6Jg,46
|
|
99
|
+
freemyid-0.2.0.dev1.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
|
100
|
+
freemyid-0.2.0.dev1.dist-info/RECORD,,
|