1. What is Notemod-selfhosted?
Notemod-selfhosted is a self-hosted edition based on the original Notemod (static UI), specialized for running on your own server with a single JSON file as the source of truth.
It was developed to enable smooth text sharing between a Windows PC and an iPhone, without relying on external services.
Using your own web server helps minimize external dependencies.
Key features
- No GitHub / Gist dependency (everything is stored in
data.jsonon your server) - Provides sync + APIs using PHP (designed for iOS Shortcuts and external tool integrations)
- Includes a Windows app (ClipboardSender) that sends clipboard text to Notemod
- Access logging supports two modes (configurable ON/OFF):
- raw file logs
- Notemod “Logs” category logs
- Automatically creates
.htaccessinnotemod-data/,config/, andlogs/to block direct access - The read API supports
pretty=2to return plain text only (body-only)
2. Directory structure (default)
public_html/
├─ index.php # Notemod UI
├─ logger.php # access logging (file / Notemod logs)
├─ notemod_sync.php # sync endpoint (save/load)
├─ api/
│ ├─ api.php # add note
│ ├─ read_api.php # read API
│ └─ cleanup_api.php # delete notes in a category (admin)
├─ notemod-data/
│ └─ data.json # single data source (runtime)
├─ config/
│ ├─ config.php # secrets + common settings (DO NOT COMMIT)
│ └─ config.api.php # API tokens + paths (DO NOT COMMIT)
└─ robots.txt # block crawlers (recommended)
config/andapi/are expected to be at the same level asindex.php.- If you change the structure, update the PHP paths accordingly.
3. Quick start (minimal setup)
3.1 Upload to your server
Upload the whole repository to your public web directory.
Example: public_html/
3.2 Create config files (IMPORTANT)
Rename and edit:
config/config.sample.php→config/config.phpconfig/config.api.sample.php→config/config.api.php
Roles:
config.php:SECRET, timezone, logger settings, etc. (sensitive)config.api.php: API tokens,data.jsonpath, etc. (sensitive)
Warning: Never commit config/config.php or config/config.api.php to GitHub.
Manage them locally / on production only, and keep them in .gitignore.
3.3 Initialize (first run only)
Open the public URL in a browser to load Notemod.
Some browsers may show a one-time initial loading error—continue anyway.
Select the UI language and create your first category. This will create:
notemod-data/data.json(initial snapshot)notemod-data/.htaccess(auto, blocks direct access)config/.htaccess(auto, blocks direct access)logs/andlogs/.htaccess(auto, only when file logging is enabled)
4. Security (very important)
4.1 Strongly recommended: Basic Auth
Running with tokens only is not recommended. Use at least one (ideally both):
- Basic Auth for the whole site and
/api/(by default/api/is accessible) - IP restriction if you can use fixed IPs
4.2 Auto-generated .htaccess (blocks direct access)
If .htaccess is missing in protected directories, Notemod-selfhosted generates the following:
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
</IfModule>
Targets (depending on environment/settings):
notemod-data/(protectsdata.json)config/(protects secrets)logs/(protects access logs)
Some shared hosts may disable or limit
.htaccess.
In that case, Basic Auth is your last line of defense.
4.3 robots.txt (anti-indexing)
A robots.txt is included to reduce the chance of being indexed by search engines.
- Note: robots is only a “polite request” and does not stop malicious access
→ Basic Auth / IP restrictions are the real protection
5. data.json structure (important)
In Notemod, categories and notes are stored as JSON strings inside data.json.
Example:
{
"categories": "[{...},{...}]",
"notes": "[{...},{...}]",
"selectedLanguage": "EN"
}
PHP requires double-decoding:
- First decode
data.jsonwithjson_decode() - Then decode
$data['notes']withjson_decode()
6. Configuration files
6.1 config/config.php (common settings / secrets)
Examples (based on the sample file):
SECRET: token for sync (recommended 16+ chars) The term “SECRET” is merely decorative and is planned to be discontinued in the future.TIMEZONE: e.g.Asia/TokyoDEBUG:truewrites logs like_sync_debug.logINITIAL_SNAPSHOT: initial data- Logger settings
LOGGER_FILE_ENABLED(file logs ON/OFF)LOGGER_NOTEMOD_ENABLED(Notemod Logs category ON/OFF)LOGGER_LOGS_DIRNAME(e.g.logs/logs1)
6.2 config/config.api.php (API settings / secrets)
Examples:
EXPECTED_TOKEN: standard API tokenADMIN_TOKEN: cleanup token (use a strong value)DATA_JSON: path tonotemod-data/data.jsonDEFAULT_COLOR: default color for created categories/notesCLEANUP_BACKUP_ENABLED: create a backup before cleanup (ON/OFF)
7. logger.php (unified access logging)
Two logging modes are supported (configurable ON/OFF):
- Raw file log:
/logs/access-YYYY-MM.log - Notemod Logs category: a monthly note
access-YYYY-MMinside categoryLogs
7.1 Logs category rules
- Category name:
Logs - Monthly note:
access-YYYY-MM - Append style: prepend to the top (newest first)
read_api.php?action=latest_notealways excludesLogs
(so you can fetch the latest “normal note” easily)
8. notemod_sync.php (server sync: save/load)
8.1 Responsibilities
action=save: saves the JSON string received from the clientaction=load: returns the JSON string stored on the server- On first run: if
data.jsonis missing, create it usingINITIAL_SNAPSHOT - If missing: generate
.htaccessinnotemod-data/andconfig/(blocks direct access)
8.2 Required parameters
token:SECRETinconfig.phpaction:save/load
9. api.php (Add Note API)
Endpoint: /api/api.php
Parameters
| Parameter | Required | Description |
|---|---|---|
token | ✓ | EXPECTED_TOKEN |
text | ✓ | Note body (multi-line allowed) |
title | optional | Defaults to timestamp if omitted |
category | optional | Defaults to INBOX |
Behavior
- If
categoryis missing,INBOXis created automatically textis stored as HTML (newlines →<br>, XSS-safe viahtmlspecialchars)- Returns JSON response
Example
https://YOUR_SITE/api/api.php?token=EXPECTED_TOKEN&category=Memo&text=AAA
10. read_api.php (Read API)
Endpoint: /api/read_api.php
10.1 Available actions
action=list_categoriesaction=list_notesaction=latest_note(always excludes theLogscategory)action=get_note(one note bycategory+title)
10.2 pretty options
pretty=1: pretty-printed JSONpretty=2: forlatest_note/get_note, returns plain text body only<br>→ newline- strips HTML tags
- decodes HTML entities
- normalizes newlines
Example:
https://YOUR_SITE/api/read_api.php?token=EXPECTED_TOKEN&action=get_note&category=Manual&title=v1.0.0&pretty=2
11. cleanup_api.php (Dangerous: delete all notes in a category)
Endpoint: /api/cleanup_api.php
Behavior
- POST only
- Requires
confirm=YES(exceptdry_run=1is allowed) - Uses
ADMIN_TOKEN(fallback:EXPECTED_TOKEN) - If
CLEANUP_BACKUP_ENABLEDis ON, createsdata.json.bak-YYYYmmdd-HHMMSSfirst
Backup
- When backups are enabled in cconfig.api.php, a backup file is created when a delete operation is executed.
- In addition to exported text files, you can also import from backup files.
Delete all backups
- The token is ADMIN_TOKEN (or falls back to EXPECTED_TOKEN)
- Use
purge_bak=1(orpurge_bak=true) - Add
dry_run=1to return only the list of targets without deleting them - When running without dry_run,
confirm=YESis still required as before
Delete all logs (.log files in the logs/ directory)
- The token is ADMIN_TOKEN (or falls back to EXPECTED_TOKEN)
- Use
purge_log=1(orpurge_log=true) - Add
dry_run=1to return only the list of targets without deleting them - When running without dry_run,
confirm=YESis still required as before
12. Basic Auth and calling the APIs
12.1 Accessing via browser
When Basic Auth is enabled, browsers typically prompt for username/password. Enter them to proceed.
The
https://user:pass@host/...format is deprecated and may be blocked by modern browsers.
12.2 Recommended: iOS Shortcuts
With “Get Contents of URL”:
- Header:
Authorization: Basic base64(user:pass) - Query/body:
token,action, etc.
If nothing happens, check:
- Header is exactly
Basic xxxx - base64 has no extra newline characters
- HTTP method is correct (GET vs POST)
13. ClipboardSender (Windows app integration)
Goal: send clipboard text to Notemod immediately, making Windows → iPhone text transfer effortless.
Typical flow
- ClipboardSender reads Windows clipboard
- Sends to
/api/api.phpwithtext=...&category=INBOX - iPhone reads via
read_api.php?action=latest_note&pretty=2
Recommended usage
- Use
INBOXas the Windows-sending category - Create an iOS Shortcut for “PC → iPhone”
- Use both Basic Auth + token for layered protection
14. Permissions (typical for shared hosting)
Typical values:
- Directories:
755 - PHP files:
644 data.json/ log files:600–644(as long as the server can write).htaccess:644
Notes:
- Writable directories:
notemod-data/, andlogs/if file logging is enabled - If the server cannot write, logger and sync will fail
15. GitHub publishing notes (must read)
Safe to publish
- PHP/HTML/JS (
index.php,api/,logger.php,notemod_sync.php) config/*.sample.php- README / LICENSE /
.gitignore/robots.txt
Never publish (DO NOT COMMIT)
config/config.phpconfig/config.api.phpnotemod-data/data.jsonlogs/or any*.log*.bak-*(backups)
16. API examples
Add a note
- GET/POST
/api/api.php?token=EXPECTED_TOKEN&category=Memo&text=AAA
Get latest note (body only)
- GET
/api/read_api.php?token=EXPECTED_TOKEN&action=latest_note&pretty=2
List notes
- GET
/api/read_api.php?token=EXPECTED_TOKEN&action=list_notes&pretty=1
Body only by category + title
- GET
/api/read_api.php?token=EXPECTED_TOKEN&action=get_note&category=aaa&title=i&pretty=2
(Legacy) Basic Auth URL examples
https://USER:PASS@YOUR_SITE/...may be blocked by modern browsers.
- GET/POST
https://USER:PASS@YOUR_SITE/api/api.php?token=EXPECTED_TOKEN&category=Memo&text=AAA - GET
https://USER:PASS@YOUR_SITE/api/read_api.php?token=EXPECTED_TOKEN&action=latest_note&pretty=2 - GET
https://USER:PASS@YOUR_SITE/api/read_api.php?token=EXPECTED_TOKEN&action=list_notes&pretty=1 - GET
https://USER:PASS@YOUR_SITE/api/read_api.php?token=EXPECTED_TOKEN&action=get_note&category=aaa&title=i&pretty=2

