vault backup: 2026-05-05 21:04:37
This commit is contained in:
parent
7042577196
commit
acb28d9df2
8 changed files with 179 additions and 1 deletions
2
.obsidian/plugins/hoarder-sync/data.json
vendored
2
.obsidian/plugins/hoarder-sync/data.json
vendored
|
|
@ -4,7 +4,7 @@
|
|||
"syncFolder": "Hoarder",
|
||||
"attachmentsFolder": "Hoarder/attachments",
|
||||
"syncIntervalMinutes": 60,
|
||||
"lastSyncTimestamp": 1778004999245,
|
||||
"lastSyncTimestamp": 1778010108899,
|
||||
"updateExistingFiles": false,
|
||||
"excludeArchived": true,
|
||||
"onlyFavorites": false,
|
||||
|
|
|
|||
|
|
@ -210,3 +210,6 @@ tags: [daily]
|
|||
- 19:50 | `aimpress`
|
||||
- **Asked:** How to manage forgotten PVE server service credentials securely?
|
||||
- **Done:** Populated Vaultwarden with discovered services and credentials, decrypting password hashes using Kali tools.
|
||||
- 21:03 (1min) | `memory-compiler`
|
||||
- **Asked:** Set up knowledge compiler for personal wiki using Karpathy's LLM schema in Obsidian.
|
||||
- **Done:** Created 3 concept articles documenting iCloud duplicates, username format, and SQLite boolean patterns.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ Same platform used by H&M. See [[wiki/client-knowledge/hm|H&M client knowledge]]
|
|||
|
||||
**Key quirk:** `sessionStorage` is used (not localStorage) — session is cleared on browser close. Users must log in again each browser session. This is intentional for security.
|
||||
|
||||
**User identity:** One2Edit usernames are email addresses in the format `FirstnameSurname@oliver.agency`. Example: Paul Johns → `PaulJohns@oliver.agency`. Look up users via `GET /api/users?clientId=<id>` — do not construct usernames by guessing. See [[wiki/concepts/one2edit-username-format|one2edit-username-format]].
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
|
|
|||
49
wiki/concepts/icloud-space-2-duplicate-files.md
Normal file
49
wiki/concepts/icloud-space-2-duplicate-files.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
title: "iCloud Concurrent Write: ' 2.md' Duplicate Files"
|
||||
aliases: [icloud-space-2, icloud-duplicate-files]
|
||||
tags: [icloud, macos, concurrency, filesystem]
|
||||
sources: [memory-compiler]
|
||||
created: 2026-05-05
|
||||
updated: 2026-05-05
|
||||
---
|
||||
|
||||
# iCloud Concurrent Write: ' 2.md' Duplicate Files
|
||||
|
||||
When two devices write to the same iCloud Drive file at the same time, iCloud resolves the conflict by keeping both versions. The second version gets a space-number suffix: `filename 2.md` (space before the number, not a hyphen or underscore).
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- Glob pattern to find duplicates: `* 2.md` (space before 2)
|
||||
- Distinct from `.git` merge conflicts — this is iCloud's own conflict resolution
|
||||
- Can silently accumulate: if not cleaned up, you may get `filename 2.md`, `filename 3.md`, etc.
|
||||
- Most common in wiki/note systems where two machines flush to iCloud roughly simultaneously
|
||||
|
||||
## When It Happens
|
||||
|
||||
- Machine 1 is writing a file while Machine 2 is also writing the same file
|
||||
- The memory-compiler multi-device workflow is a classic trigger: flush.py on Machine 2 writes the daily log; compile.py on Machine 1 also writes to wiki notes — both during the same ~21:00 window
|
||||
|
||||
## Detection & Cleanup
|
||||
|
||||
```bash
|
||||
# Find all iCloud duplicate files (space-2 suffix)
|
||||
find . -name "* 2.md"
|
||||
|
||||
# Or with glob (zsh)
|
||||
ls **/*\ 2.md
|
||||
```
|
||||
|
||||
The memory-compiler runs a LaunchAgent dedup at **09:00** and **21:30** that removes these duplicates automatically.
|
||||
|
||||
## Distinction from icloud-git-sync-conflict
|
||||
|
||||
| Scenario | File produced |
|
||||
|----------|--------------|
|
||||
| iCloud concurrent write (any file) | `filename 2.md` |
|
||||
| git conflict in iCloud-synced repo | `filename.md` with `<<<<<<` markers |
|
||||
|
||||
See [[wiki/concepts/icloud-git-sync-conflict|icloud-git-sync-conflict]] for the git-specific variant.
|
||||
|
||||
## Related
|
||||
|
||||
- [[wiki/concepts/icloud-git-sync-conflict|icloud-git-sync-conflict]] — git conflict markers in iCloud-synced repos
|
||||
48
wiki/concepts/one2edit-username-format.md
Normal file
48
wiki/concepts/one2edit-username-format.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: "One2Edit Username Format"
|
||||
aliases: [one2edit-username, one2edit-users-api]
|
||||
tags: [one2edit, api, auth, 3m, hm]
|
||||
sources: [01 Projects/3m-portal]
|
||||
created: 2026-05-05
|
||||
updated: 2026-05-05
|
||||
---
|
||||
|
||||
# One2Edit Username Format
|
||||
|
||||
One2Edit usernames are **email addresses**, not `firstname.lastname` handles. This trips up anyone guessing usernames from employee names.
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- Username format: `FirstnameSurname@oliver.agency` (no dot, no hyphen)
|
||||
- Example: Paul Johns → `PaulJohns@oliver.agency`
|
||||
- Do NOT guess `paul.johns` or `paul.johns@oliver.agency` — these do not exist
|
||||
- Use the users API to look up valid usernames, don't guess
|
||||
|
||||
## Users API
|
||||
|
||||
```
|
||||
GET /api/users?clientId=<id>
|
||||
```
|
||||
|
||||
- Requires `clientId` parameter — the request will fail or return nothing without it
|
||||
- Authenticate with service account (`portal@oliver.agency`) via the CORS proxy before calling
|
||||
- Returns a list of users for the given client, each with their email-format username
|
||||
|
||||
### Lookup Flow
|
||||
|
||||
```
|
||||
1. Auth: POST /Api.php → externSessionId (service account)
|
||||
2. Fetch: GET /api/users?clientId=<clientId> → [{username, ...}, ...]
|
||||
3. Use: pass username (email) to login/session endpoints
|
||||
```
|
||||
|
||||
## Common Mistake
|
||||
|
||||
> "I'll just try `paul.johns` or `p.johns@oliver.agency`"
|
||||
|
||||
Neither works. The canonical username is the email without dots: `PaulJohns@oliver.agency`. Always look it up from the API rather than constructing it from a name.
|
||||
|
||||
## Related
|
||||
|
||||
- [[wiki/tech-patterns/one2edit-api|one2edit-api]] — full One2Edit API integration pattern
|
||||
- [[wiki/client-knowledge/3m|3m]] — 3M OMG Portal where this pattern applies
|
||||
58
wiki/concepts/sqlite-not-null-as-boolean.md
Normal file
58
wiki/concepts/sqlite-not-null-as-boolean.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: "SQLite: IS NOT NULL AS Boolean"
|
||||
aliases: [sqlite-not-null-boolean, sqlite-derived-boolean]
|
||||
tags: [sqlite, sql, security, pattern]
|
||||
sources: [sandbox]
|
||||
created: 2026-05-05
|
||||
updated: 2026-05-05
|
||||
---
|
||||
|
||||
# SQLite: IS NOT NULL AS Boolean
|
||||
|
||||
When a column contains sensitive data (e.g. a password hash), never select it directly to answer a yes/no question. Use `(column IS NOT NULL) AS flag` to return a derived boolean instead.
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- `(password_hash IS NOT NULL) AS has_password` → returns `1` or `0`, never the hash
|
||||
- Hash never leaves the database layer — client gets the correct boolean
|
||||
- Standard pattern for any column that must stay server-side
|
||||
|
||||
## Pattern
|
||||
|
||||
```sql
|
||||
-- BAD: leaks the hash to the application layer
|
||||
SELECT id, email, password_hash FROM users WHERE id = ?;
|
||||
|
||||
-- GOOD: returns a boolean, hash stays in DB
|
||||
SELECT id, email, (password_hash IS NOT NULL) AS has_password FROM users WHERE id = ?;
|
||||
```
|
||||
|
||||
In SQLite, the expression evaluates to `1` (true) or `0` (false). Map to a JS/Python boolean at the application layer:
|
||||
|
||||
```js
|
||||
// Node.js / better-sqlite3
|
||||
const user = db.prepare(`
|
||||
SELECT id, email, (password_hash IS NOT NULL) AS has_password
|
||||
FROM users WHERE id = ?
|
||||
`).get(userId);
|
||||
|
||||
// SQLite returns 1/0; coerce to boolean
|
||||
user.hasPassword = Boolean(user.has_password);
|
||||
```
|
||||
|
||||
## Bug This Fixes
|
||||
|
||||
The `userPublic()` helper was selecting `u.password_hash` directly. The result object included the raw hash — or, worse, `undefined` when the column was NULL — causing `hasPassword` to always evaluate to `false` (because the hash value was truthy but the field name wasn't mapped correctly).
|
||||
|
||||
Fix: replace the direct column with `(password_hash IS NOT NULL) AS has_password`.
|
||||
|
||||
## Generalisation
|
||||
|
||||
The same pattern applies to any secret or large binary:
|
||||
- `(api_key IS NOT NULL) AS has_api_key`
|
||||
- `(avatar_blob IS NOT NULL) AS has_avatar`
|
||||
- `(refresh_token IS NOT NULL) AS is_linked`
|
||||
|
||||
## Related
|
||||
|
||||
- [[wiki/concepts/fastapi-response-model-silent-field-strip|fastapi-response-model-silent-field-strip]] — similar idea: strip sensitive fields at the serialization layer
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
|
||||
# Build Log
|
||||
|
||||
## [2026-05-05T21:00:00+01:00] compile | 2026-05-05.md (pass 2)
|
||||
- Source: daily/2026-05-05.md
|
||||
- Articles created: [[wiki/concepts/icloud-space-2-duplicate-files]], [[wiki/concepts/one2edit-username-format]], [[wiki/concepts/sqlite-not-null-as-boolean]]
|
||||
- Articles updated: [[wiki/tech-patterns/one2edit-api]] (added Users API section + username=email gotcha); [[wiki/client-knowledge/3m]] (added One2Edit user identity note)
|
||||
|
||||
## [2026-05-05T23:59:00+01:00] compile | 2026-05-05.md
|
||||
- Source: daily/2026-05-05.md
|
||||
- Articles created: [[wiki/concepts/vite-prebuilt-subpath-workaround]], [[wiki/concepts/llamaextract-data-none-gotcha]]
|
||||
|
|
|
|||
|
|
@ -53,7 +53,20 @@ Any client project built on the One2Edit platform (3M, H&M).
|
|||
- [[01 Projects/3m-portal/3M OMG Portal|3M OMG Portal]] — Full portal: CORS proxy + Node.js backend + embedded SDK
|
||||
- [[01 Projects/hm-o2e-tool/HM O2E Tool|H&M O2E Tool]] — Static tool: image relinking + document export (no proxy needed — called directly or via `python -m http.server`)
|
||||
|
||||
## Users API
|
||||
|
||||
```
|
||||
GET /api/users?clientId=<id>
|
||||
```
|
||||
|
||||
- `clientId` is **required** — the request silently fails or returns nothing without it
|
||||
- Auth with service account session before calling
|
||||
- Returns user list with email-format usernames
|
||||
|
||||
See [[wiki/concepts/one2edit-username-format|one2edit-username-format]] for full details.
|
||||
|
||||
## Gotchas & Lessons
|
||||
- **Username format is an email address** — `FirstnameSurname@oliver.agency` (e.g. `PaulJohns@oliver.agency`). Never guess `firstname.lastname` — it doesn't exist as a format
|
||||
- 301/302 redirects from One2Edit mean auth failure — the Node proxy converts them to 401 to prevent redirect loops in the browser
|
||||
- `sessionStorage` (not `localStorage`) — sessions clear on browser close, which is correct for this auth model
|
||||
- H&M O2E tool is static (no backend) — can run without a server for most operations
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue