chore: scaffold chatelet LNbits extension
Nostr-native room rentals (Airbnb-style) for the castle. Establishes the extension manifest, license, README with the design overview, and static + template placeholders. No booking logic yet — subsequent commits build the model, schema, arbiter, Nostr layer, API, tasks, and wiring bottom-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD
This commit is contained in:
commit
c756ac7cf6
6 changed files with 137 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
.venv/
|
||||||
|
node_modules/
|
||||||
|
*.sqlite3
|
||||||
|
.DS_Store
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 aiolabs
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
53
README.md
Normal file
53
README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Chatelet
|
||||||
|
|
||||||
|
Nostr-native room rentals for LNbits — an "Airbnb for the castle". List
|
||||||
|
rooms, take booking requests, arbitrate availability, and settle stays over
|
||||||
|
Lightning, with Nostr as the interop layer.
|
||||||
|
|
||||||
|
> **Status:** design sketch + scaffold. Data model, migrations, availability
|
||||||
|
> arbiter, REST surface, and Nostr event model are in place; relay plumbing,
|
||||||
|
> FX/invoice wiring, and the guest UI are stubbed with `TODO(...)` markers.
|
||||||
|
|
||||||
|
## Why not just reuse an existing NIP?
|
||||||
|
|
||||||
|
No single NIP models a rental-booking flow. Chatelet **composes** existing
|
||||||
|
NIPs and only invents an event kind where nothing fits:
|
||||||
|
|
||||||
|
| Concern | Mechanism |
|
||||||
|
|---|---|
|
||||||
|
| Room listing | **NIP-99** classified listing (`kind:30402`) |
|
||||||
|
| Guest's reservation record | **NIP-78** app data (`kind:30078`), NIP-44 encrypted |
|
||||||
|
| Public availability calendar | **NIP-52** (`kind:31923/31924`), no guest PII |
|
||||||
|
| Private request → quote → confirm | **NIP-17/59** giftwrapped DMs |
|
||||||
|
| Live "is it free?" query | aiolabs band **`kind:22000/22001`** (ephemeral) |
|
||||||
|
| Payment / deposit | LNbits Lightning invoice |
|
||||||
|
|
||||||
|
Full rationale in [`docs/adr-0001-nostr-event-model.md`](docs/adr-0001-nostr-event-model.md).
|
||||||
|
|
||||||
|
## The one invariant that matters
|
||||||
|
|
||||||
|
**LNbits is the booking authority; Nostr events are requests, not locks.**
|
||||||
|
Availability is *derived* from the DB (`bookings` + `blocks`), never stored
|
||||||
|
as a positive fact, and a date range is only truly locked when a `held`
|
||||||
|
booking row is written. Payment — not any Nostr event — promotes a booking
|
||||||
|
to `confirmed`.
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [`docs/data-model.md`](docs/data-model.md) — tables, entities, lifecycle
|
||||||
|
- [`docs/event-flow.md`](docs/event-flow.md) — who publishes what, when (with sequence diagram)
|
||||||
|
- [`docs/adr-0001-nostr-event-model.md`](docs/adr-0001-nostr-event-model.md) — why these kinds
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
config.json LNbits extension manifest
|
||||||
|
models.py data model (pydantic)
|
||||||
|
migrations.py schema (ext_chatelet DB)
|
||||||
|
crud.py persistence + the availability arbiter
|
||||||
|
views_api.py REST surface (shares the booking flow with Nostr)
|
||||||
|
views.py operator admin page route
|
||||||
|
tasks.py invoice listener (settle) + hold-expiry sweep
|
||||||
|
nostr/ event kinds, builders, sign/publish/subscribe service
|
||||||
|
docs/ design docs
|
||||||
|
```
|
||||||
23
config.json
Normal file
23
config.json
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"id": "chatelet",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"name": "Chatelet",
|
||||||
|
"repo": "https://git.atitlan.io/aiolabs/chatelet",
|
||||||
|
"short_description": "Nostr-native room rentals (Airbnb-style) for LNbits",
|
||||||
|
"description": "",
|
||||||
|
"tile": "/chatelet/static/image/chatelet.png",
|
||||||
|
"min_lnbits_version": "1.4.0",
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "padreug",
|
||||||
|
"uri": "https://git.atitlan.io/padreug",
|
||||||
|
"role": "Author"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": [],
|
||||||
|
"license": "MIT",
|
||||||
|
"paid_features": "",
|
||||||
|
"tags": ["Merchant", "Nostr"],
|
||||||
|
"donate": "",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
15
static/js/index.js
Normal file
15
static/js/index.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Chatelet operator admin page — placeholder.
|
||||||
|
// Quasar 2 + Vue 3 as UMD globals (no build step). Remember: no
|
||||||
|
// self-closing tags in UMD templates, and use :style bindings (not <style>
|
||||||
|
// blocks) for per-element typography overrides on LNbits pages.
|
||||||
|
window.app = Vue.createApp({
|
||||||
|
el: '#vue',
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
rooms: [],
|
||||||
|
bookings: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO: wire GET /chatelet/api/v1/rooms, room CRUD, calendar, bookings.
|
||||||
|
})
|
||||||
18
templates/chatelet/index.html
Normal file
18
templates/chatelet/index.html
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context %}
|
||||||
|
{% block page %}
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<h5 class="text-subtitle1 q-my-none">Chatelet — Room Rentals</h5>
|
||||||
|
<p class="text-caption">
|
||||||
|
Nostr-native room rentals. Operator admin UI goes here (rooms,
|
||||||
|
calendar, bookings). Guest booking UI lives in the webapp.
|
||||||
|
</p>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||||
|
<script src="{{ static_url_for('chatelet/static', 'js/index.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue