@archlast/server 0.0.1 → 0.1.0

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.
package/README.md CHANGED
@@ -1,32 +1,801 @@
1
1
  # @archlast/server
2
2
 
3
- Archlast server library exports (schema, functions, types) plus Docker
4
- runtime metadata and templates.
3
+ Type-safe server definitions and runtime helpers for Archlast. This package provides schema definition with validators, function definitions (query, mutation, action, http, webhook, rpc), background job scheduling, and shared types. The runtime server is delivered via Docker image.
5
4
 
6
- ## Install
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Library vs Runtime](#library-vs-runtime)
9
+ - [Schema Definition](#schema-definition)
10
+ - [Field Types (Validators)](#field-types-validators)
11
+ - [Field Modifiers](#field-modifiers)
12
+ - [Relationships](#relationships)
13
+ - [Indexes](#indexes)
14
+ - [Function Definitions](#function-definitions)
15
+ - [Query](#query)
16
+ - [Mutation](#mutation)
17
+ - [Action](#action)
18
+ - [HTTP Routes](#http-routes)
19
+ - [Webhooks](#webhooks)
20
+ - [RPC (tRPC-style)](#rpc-trpc-style)
21
+ - [Context Objects](#context-objects)
22
+ - [Authentication & Permissions](#authentication--permissions)
23
+ - [Background Jobs](#background-jobs)
24
+ - [Storage Types](#storage-types)
25
+ - [Subpath Exports](#subpath-exports)
26
+ - [Environment Variables](#environment-variables)
27
+ - [Docker Templates](#docker-templates)
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install -D @archlast/server
33
+
34
+ # Or with other package managers
35
+ pnpm add -D @archlast/server
36
+ yarn add -D @archlast/server
37
+ bun add -D @archlast/server
38
+ ```
39
+
40
+ ## Library vs Runtime
41
+
42
+ This package is a **dev-time library** for schema and function definitions. The actual server runtime is delivered via Docker and managed by the CLI:
7
43
 
8
44
  ```bash
9
- npm install @archlast/server --save-dev
45
+ # Use the CLI to run the server
46
+ archlast start
47
+
48
+ # The Docker image contains the full runtime
49
+ docker pull archlast/server:latest
10
50
  ```
11
51
 
12
- ## Library usage
52
+ ---
53
+
54
+ ## Schema Definition
55
+
56
+ Define your data model in `src/schema.ts`:
13
57
 
14
58
  ```ts
15
59
  import { defineSchema, defineTable, v } from "@archlast/server/schema/definition";
16
60
 
17
61
  export default defineSchema({
18
62
  tasks: defineTable({
19
- id: v.id(),
63
+ _id: v.id().primaryKey(),
20
64
  text: v.string(),
65
+ completed: v.boolean().default(false),
66
+ priority: v.string().default("medium"),
67
+ createdAt: v.number().createdNow(),
68
+ updatedAt: v.number().updateNow().optional(),
21
69
  }),
70
+
71
+ users: defineTable({
72
+ _id: v.id().primaryKey(),
73
+ email: v.string().unique(),
74
+ name: v.string(),
75
+ role: v.string().default("user"),
76
+ }),
77
+ });
78
+ ```
79
+
80
+ ### Field Types (Validators)
81
+
82
+ The `v` object provides all available field types:
83
+
84
+ | Validator | TypeScript Type | Description |
85
+ |-----------|-----------------|-------------|
86
+ | `v.id()` | `string` | CUID2 identifier (auto-generated) |
87
+ | `v.uuid()` | `string` | UUID v4 identifier |
88
+ | `v.objectId()` | `string` | MongoDB-style ObjectId |
89
+ | `v.string()` | `string` | Text field |
90
+ | `v.number()` | `number` | Numeric field (integer or float) |
91
+ | `v.boolean()` | `boolean` | True/false field |
92
+ | `v.date()` | `Date` | Date object |
93
+ | `v.datetime()` | `Date` | Date with time |
94
+ | `v.bytes()` | `Uint8Array` | Binary data |
95
+ | `v.json()` | `unknown` | Arbitrary JSON |
96
+ | `v.any()` | `any` | Any type (use sparingly) |
97
+ | `v.object(shape)` | `object` | Nested object with schema |
98
+ | `v.array(schema)` | `T[]` | Array of items |
99
+ | `v.optional(type)` | `T \| undefined` | Optional wrapper |
100
+
101
+ ### Examples
102
+
103
+ ```ts
104
+ import { v } from "@archlast/server/schema/validators";
105
+
106
+ // Basic types
107
+ v.string() // string
108
+ v.number() // number
109
+ v.boolean() // boolean
110
+
111
+ // IDs
112
+ v.id() // CUID2: "clxxxx..."
113
+ v.uuid() // UUID: "550e8400-e29b-41d4-a716-446655440000"
114
+ v.objectId() // ObjectId: "507f1f77bcf86cd799439011"
115
+
116
+ // Dates
117
+ v.date() // Date object (date only)
118
+ v.datetime() // Date object (with time)
119
+
120
+ // Complex types
121
+ v.json() // Any JSON-serializable value
122
+ v.bytes() // Binary data as Uint8Array
123
+
124
+ // Nested object
125
+ v.object({
126
+ street: v.string(),
127
+ city: v.string(),
128
+ zip: v.string(),
129
+ })
130
+
131
+ // Array
132
+ v.array(v.string()) // string[]
133
+ v.array(v.object({ // Array of objects
134
+ name: v.string(),
135
+ score: v.number(),
136
+ }))
137
+ ```
138
+
139
+ ### Field Modifiers
140
+
141
+ Chain modifiers to add constraints and behaviors:
142
+
143
+ | Modifier | Description |
144
+ |----------|-------------|
145
+ | `.primaryKey()` | Mark as primary key (auto-generates if not provided) |
146
+ | `.unique()` | Unique constraint |
147
+ | `.index()` | Create index for faster queries |
148
+ | `.searchable()` | Enable full-text search |
149
+ | `.optional()` | Make field nullable (TypeScript: `T \| undefined`) |
150
+ | `.default(value)` | Default value on insert |
151
+ | `.createdNow()` | Auto-set to `Date.now()` on insert |
152
+ | `.updateNow()` | Auto-set to `Date.now()` on every update |
153
+ | `.foreignKey(table, field, options)` | Foreign key reference |
154
+
155
+ ### Examples
156
+
157
+ ```ts
158
+ defineTable({
159
+ // Primary key with auto-generation
160
+ _id: v.id().primaryKey(),
161
+
162
+ // Unique constraint
163
+ email: v.string().unique(),
164
+
165
+ // Indexed for fast lookups
166
+ slug: v.string().index(),
167
+
168
+ // Full-text searchable
169
+ content: v.string().searchable(),
170
+
171
+ // Optional field
172
+ bio: v.string().optional(),
173
+
174
+ // Default values
175
+ role: v.string().default("user"),
176
+ count: v.number().default(0),
177
+ active: v.boolean().default(true),
178
+
179
+ // Automatic timestamps
180
+ createdAt: v.number().createdNow(),
181
+ updatedAt: v.number().updateNow().optional(),
182
+
183
+ // Foreign key
184
+ authorId: v.id().foreignKey("users", "_id", {
185
+ onDelete: "cascade",
186
+ onUpdate: "cascade",
187
+ }),
188
+ })
189
+ ```
190
+
191
+ ### Relationships
192
+
193
+ Define relationships in the second argument of `defineTable`:
194
+
195
+ ```ts
196
+ import {
197
+ defineTable,
198
+ hasOne,
199
+ hasMany,
200
+ belongsTo,
201
+ manyToMany
202
+ } from "@archlast/server/schema/definition";
203
+
204
+ export default defineSchema({
205
+ users: defineTable(
206
+ {
207
+ _id: v.id().primaryKey(),
208
+ name: v.string(),
209
+ },
210
+ {
211
+ relationships: {
212
+ // User has many tasks
213
+ tasks: hasMany("tasks", "authorId"),
214
+
215
+ // User has one profile
216
+ profile: hasOne("profiles", "userId"),
217
+ },
218
+ }
219
+ ),
220
+
221
+ tasks: defineTable(
222
+ {
223
+ _id: v.id().primaryKey(),
224
+ text: v.string(),
225
+ authorId: v.id(),
226
+ },
227
+ {
228
+ relationships: {
229
+ // Task belongs to user
230
+ author: belongsTo("users", "authorId"),
231
+
232
+ // Task has many subtasks
233
+ subtasks: hasMany("subtasks", "taskId"),
234
+ },
235
+ }
236
+ ),
237
+
238
+ tags: defineTable(
239
+ {
240
+ _id: v.id().primaryKey(),
241
+ name: v.string(),
242
+ },
243
+ {
244
+ relationships: {
245
+ // Many-to-many through junction table
246
+ tasks: manyToMany("tasks", "task_tags", "tagId", "taskId"),
247
+ },
248
+ }
249
+ ),
250
+ });
251
+ ```
252
+
253
+ ### Indexes
254
+
255
+ Create composite indexes for complex queries:
256
+
257
+ ```ts
258
+ defineTable(
259
+ {
260
+ _id: v.id().primaryKey(),
261
+ completed: v.boolean(),
262
+ priority: v.string(),
263
+ createdAt: v.number(),
264
+ },
265
+ {
266
+ indexes: [
267
+ // Composite index
268
+ { fields: ["completed", "createdAt"], name: "idx_tasks_status" },
269
+
270
+ // Single field index
271
+ { fields: ["priority"], name: "idx_tasks_priority" },
272
+ ],
273
+ }
274
+ )
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Function Definitions
280
+
281
+ Functions are the API of your Archlast application. Import from `@archlast/server/functions/definition`.
282
+
283
+ ### Query
284
+
285
+ Read-only operations. Results are cached and reactive.
286
+
287
+ ```ts
288
+ import { query } from "@archlast/server/functions/definition";
289
+
290
+ // Simple query
291
+ export const list = query(async (ctx) => {
292
+ return ctx.db.query("tasks").findMany();
293
+ });
294
+
295
+ // Query with arguments
296
+ export const get = query({
297
+ args: { id: v.string().zodSchema },
298
+ handler: async (ctx, args) => {
299
+ return ctx.db.get("tasks", args.id);
300
+ },
301
+ });
302
+
303
+ // Query with filtering
304
+ export const listByStatus = query({
305
+ args: {
306
+ completed: v.boolean().zodSchema,
307
+ limit: v.number().optional().zodSchema,
308
+ },
309
+ handler: async (ctx, args) => {
310
+ return ctx.db
311
+ .query("tasks")
312
+ .where({ completed: args.completed })
313
+ .take(args.limit ?? 10)
314
+ .findMany();
315
+ },
316
+ });
317
+ ```
318
+
319
+ ### Mutation
320
+
321
+ Write operations. Automatically invalidate related caches.
322
+
323
+ ```ts
324
+ import { mutation } from "@archlast/server/functions/definition";
325
+
326
+ export const create = mutation({
327
+ args: {
328
+ text: v.string().zodSchema,
329
+ priority: v.string().optional().zodSchema,
330
+ },
331
+ handler: async (ctx, args) => {
332
+ return ctx.db.insert("tasks", {
333
+ text: args.text,
334
+ priority: args.priority ?? "medium",
335
+ completed: false,
336
+ });
337
+ },
338
+ });
339
+
340
+ export const update = mutation({
341
+ args: {
342
+ id: v.string().zodSchema,
343
+ text: v.string().optional().zodSchema,
344
+ completed: v.boolean().optional().zodSchema,
345
+ },
346
+ handler: async (ctx, args) => {
347
+ const { id, ...data } = args;
348
+ return ctx.db.update("tasks", id, data);
349
+ },
350
+ });
351
+
352
+ export const remove = mutation({
353
+ args: { id: v.string().zodSchema },
354
+ handler: async (ctx, args) => {
355
+ await ctx.db.delete("tasks", args.id);
356
+ return { success: true };
357
+ },
358
+ });
359
+ ```
360
+
361
+ ### Action
362
+
363
+ Long-running operations, side effects, external API calls.
364
+
365
+ ```ts
366
+ import { action } from "@archlast/server/functions/definition";
367
+
368
+ export const sendEmail = action({
369
+ args: {
370
+ to: v.string().zodSchema,
371
+ subject: v.string().zodSchema,
372
+ body: v.string().zodSchema,
373
+ },
374
+ handler: async (ctx, args) => {
375
+ // Call external email service
376
+ const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
377
+ method: "POST",
378
+ headers: {
379
+ "Authorization": `Bearer ${process.env.SENDGRID_API_KEY}`,
380
+ "Content-Type": "application/json",
381
+ },
382
+ body: JSON.stringify({
383
+ personalizations: [{ to: [{ email: args.to }] }],
384
+ from: { email: "noreply@myapp.com" },
385
+ subject: args.subject,
386
+ content: [{ type: "text/plain", value: args.body }],
387
+ }),
388
+ });
389
+
390
+ return { sent: response.ok };
391
+ },
392
+ });
393
+
394
+ export const processImage = action({
395
+ args: { storageId: v.string().zodSchema },
396
+ handler: async (ctx, args) => {
397
+ const file = await ctx.storage.get(args.storageId);
398
+ // Process image...
399
+ const processedId = await ctx.storage.store("processed", processedBuffer);
400
+ return { processedId };
401
+ },
402
+ });
403
+ ```
404
+
405
+ ### HTTP Routes
406
+
407
+ Explicit HTTP endpoints with full control over request/response.
408
+
409
+ ```ts
410
+ import { http } from "@archlast/server/functions/definition";
411
+
412
+ // GET route
413
+ export const health = http.get({
414
+ path: "/health",
415
+ handler: async () => {
416
+ return new Response("ok", { status: 200 });
417
+ },
418
+ });
419
+
420
+ // POST route with body parsing
421
+ export const createWebhook = http.post({
422
+ path: "/api/webhooks",
423
+ handler: async (ctx, request) => {
424
+ const body = await request.json();
425
+ // Process webhook...
426
+ return Response.json({ received: true });
427
+ },
428
+ });
429
+
430
+ // Route with path parameters
431
+ export const getUser = http.get({
432
+ path: "/api/users/:id",
433
+ handler: async (ctx, request, params) => {
434
+ const user = await ctx.db.get("users", params.id);
435
+ if (!user) {
436
+ return new Response("Not found", { status: 404 });
437
+ }
438
+ return Response.json(user);
439
+ },
440
+ });
441
+
442
+ // Other methods
443
+ http.put({ path: "/api/resource/:id", handler: async (ctx, req) => { /* ... */ } });
444
+ http.patch({ path: "/api/resource/:id", handler: async (ctx, req) => { /* ... */ } });
445
+ http.delete({ path: "/api/resource/:id", handler: async (ctx, req) => { /* ... */ } });
446
+ http.options({ path: "/api/resource", handler: async () => { /* ... */ } });
447
+ ```
448
+
449
+ ### Webhooks
450
+
451
+ Signed webhook handlers with automatic verification.
452
+
453
+ ```ts
454
+ import { webhook } from "@archlast/server/functions/definition";
455
+
456
+ export const stripeWebhook = webhook({
457
+ path: "/webhooks/stripe",
458
+ secret: process.env.STRIPE_WEBHOOK_SECRET!,
459
+ handler: async (ctx, event) => {
460
+ switch (event.type) {
461
+ case "checkout.session.completed":
462
+ await handleCheckoutComplete(ctx, event.data);
463
+ break;
464
+ case "customer.subscription.deleted":
465
+ await handleSubscriptionCanceled(ctx, event.data);
466
+ break;
467
+ }
468
+ return { received: true };
469
+ },
470
+ });
471
+
472
+ export const githubWebhook = webhook({
473
+ path: "/webhooks/github",
474
+ secret: process.env.GITHUB_WEBHOOK_SECRET!,
475
+ signatureHeader: "X-Hub-Signature-256",
476
+ handler: async (ctx, payload) => {
477
+ console.log("GitHub event:", payload.action);
478
+ return { ok: true };
479
+ },
22
480
  });
23
481
  ```
24
482
 
25
- ## Docker templates
483
+ ### RPC (tRPC-style)
484
+
485
+ Type-safe RPC procedures with automatic router generation.
26
486
 
27
- Docker compose templates and configuration examples are available under
28
- `templates/` in this package.
487
+ ```ts
488
+ import { rpc } from "@archlast/server/functions/definition";
489
+
490
+ // RPC query
491
+ export const getTasks = rpc.query({
492
+ args: { completed: v.boolean().optional().zodSchema },
493
+ handler: async (ctx, args) => {
494
+ return ctx.db.query("tasks")
495
+ .where(args.completed !== undefined ? { completed: args.completed } : {})
496
+ .findMany();
497
+ },
498
+ });
29
499
 
30
- ## Publishing (maintainers)
500
+ // RPC mutation
501
+ export const createTask = rpc.mutation({
502
+ args: { text: v.string().zodSchema },
503
+ handler: async (ctx, args) => {
504
+ return ctx.db.insert("tasks", { text: args.text });
505
+ },
506
+ });
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Context Objects
512
+
513
+ Each function receives a context object with available services.
514
+
515
+ ### QueryCtx (for queries)
516
+
517
+ ```ts
518
+ interface QueryCtx {
519
+ db: GenericDatabaseReader<DataModel>;
520
+ auth: AuthContext;
521
+ repository: RepositoryMap<DataModel>;
522
+ logger: LogService;
523
+ }
524
+ ```
525
+
526
+ ### MutationCtx (for mutations)
527
+
528
+ ```ts
529
+ interface MutationCtx extends QueryCtx {
530
+ db: GenericDatabaseWriter<DataModel>;
531
+ scheduler: JobScheduler;
532
+ }
533
+ ```
534
+
535
+ ### ActionCtx (for actions)
536
+
537
+ ```ts
538
+ interface ActionCtx extends MutationCtx {
539
+ storage: StorageClient;
540
+ }
541
+ ```
542
+
543
+ ### AuthContext
544
+
545
+ ```ts
546
+ interface AuthContext {
547
+ userId: string | null;
548
+ sessionId: string | null;
549
+ isAuthenticated: boolean;
550
+ user: User | null;
551
+ }
552
+ ```
553
+
554
+ ### Usage Examples
555
+
556
+ ```ts
557
+ export const myQuery = query(async (ctx) => {
558
+ // Database access
559
+ const tasks = await ctx.db.query("tasks").findMany();
560
+
561
+ // LINQ-style repository
562
+ const data = await ctx.repository.tasks
563
+ .Where({ completed: false })
564
+ .OrderBy({ createdAt: "desc" })
565
+ .Take(10)
566
+ .ToArrayAsync();
567
+
568
+ // Auth info
569
+ if (ctx.auth.isAuthenticated) {
570
+ console.log("User:", ctx.auth.userId);
571
+ }
572
+
573
+ // Logging
574
+ ctx.logger.info("Query executed", { count: tasks.length });
575
+
576
+ return tasks;
577
+ });
578
+
579
+ export const myMutation = mutation(async (ctx) => {
580
+ // Schedule background job
581
+ await ctx.scheduler.schedule({
582
+ name: "sendNotification",
583
+ preset: SchedulePreset.RunNow,
584
+ payload: { userId: ctx.auth.userId },
585
+ });
586
+ });
587
+
588
+ export const myAction = action(async (ctx) => {
589
+ // File storage
590
+ const file = await ctx.storage.get("file_id");
591
+ const newId = await ctx.storage.store("bucket", buffer);
592
+ });
593
+ ```
594
+
595
+ ---
596
+
597
+ ## Authentication & Permissions
598
+
599
+ ### Auth Modes
600
+
601
+ All functions default to requiring authentication. Override with `auth` option:
602
+
603
+ ```ts
604
+ // Required (default) - must be authenticated
605
+ export const privateQuery = query({
606
+ handler: async (ctx) => {
607
+ // ctx.auth.userId is guaranteed to exist
608
+ },
609
+ });
610
+
611
+ // Optional - authentication checked but not required
612
+ export const optionalAuth = query({
613
+ auth: "optional",
614
+ handler: async (ctx) => {
615
+ if (ctx.auth.isAuthenticated) {
616
+ // Authenticated user
617
+ } else {
618
+ // Anonymous user
619
+ }
620
+ },
621
+ });
622
+
623
+ // Public - no authentication required
624
+ export const publicQuery = query({
625
+ auth: "public",
626
+ handler: async (ctx) => {
627
+ // Anyone can access
628
+ },
629
+ });
630
+ ```
631
+
632
+ ### Permissions
633
+
634
+ Add permission checks for role-based access:
635
+
636
+ ```ts
637
+ export const adminOnly = query({
638
+ permissions: ["admin"],
639
+ handler: async (ctx) => {
640
+ // Only users with "admin" permission
641
+ },
642
+ });
643
+
644
+ export const moderatorOrAdmin = mutation({
645
+ permissions: ["admin", "moderator"],
646
+ handler: async (ctx) => {
647
+ // Users with either permission
648
+ },
649
+ });
650
+ ```
651
+
652
+ ---
653
+
654
+ ## Background Jobs
655
+
656
+ Schedule background tasks using the job scheduler.
657
+
658
+ ```ts
659
+ import { SchedulePreset } from "@archlast/server/jobs";
660
+
661
+ export const scheduleCleanup = mutation(async (ctx) => {
662
+ // Run immediately
663
+ await ctx.scheduler.schedule({
664
+ name: "cleanupOldRecords",
665
+ preset: SchedulePreset.RunNow,
666
+ payload: { olderThanDays: 30 },
667
+ });
668
+
669
+ // Run in 5 minutes
670
+ await ctx.scheduler.schedule({
671
+ name: "sendReminder",
672
+ preset: SchedulePreset.In5Minutes,
673
+ payload: { userId: ctx.auth.userId },
674
+ });
675
+
676
+ // Recurring with cron
677
+ await ctx.scheduler.schedule({
678
+ name: "dailyReport",
679
+ cron: "0 9 * * *", // Every day at 9 AM
680
+ payload: {},
681
+ });
682
+ });
683
+ ```
684
+
685
+ ### Schedule Presets
686
+
687
+ | Preset | Description |
688
+ |--------|-------------|
689
+ | `RunNow` | Execute immediately |
690
+ | `EveryMinute` | Run every minute |
691
+ | `Every5Minutes` | Run every 5 minutes |
692
+ | `Every15Minutes` | Run every 15 minutes |
693
+ | `Every30Minutes` | Run every 30 minutes |
694
+ | `Hourly` | Run every hour |
695
+ | `Every6Hours` | Run every 6 hours |
696
+ | `Every12Hours` | Run every 12 hours |
697
+ | `DailyMidnight` | Daily at midnight UTC |
698
+ | `DailyNoon` | Daily at noon UTC |
699
+ | `Weekly` | Weekly on Sunday midnight |
700
+ | `Monthly` | Monthly on the 1st |
701
+ | `In5Minutes` | Once, 5 minutes from now |
702
+ | `In1Hour` | Once, 1 hour from now |
703
+ | `In1Day` | Once, 24 hours from now |
704
+
705
+ ---
706
+
707
+ ## Storage Types
708
+
709
+ File storage type definitions.
710
+
711
+ ```ts
712
+ import type { StorageFile, StorageMetadata } from "@archlast/server/storage/types";
713
+
714
+ interface StorageFile {
715
+ id: string;
716
+ bucket: string;
717
+ filename: string;
718
+ contentType: string;
719
+ size: number;
720
+ createdAt: Date;
721
+ }
722
+
723
+ interface StorageMetadata {
724
+ filename: string;
725
+ contentType: string;
726
+ size: number;
727
+ bucket: string;
728
+ createdAt: Date;
729
+ url?: string;
730
+ }
731
+ ```
732
+
733
+ ---
734
+
735
+ ## Subpath Exports
736
+
737
+ Import only what you need:
738
+
739
+ | Import | Description |
740
+ |--------|-------------|
741
+ | `@archlast/server/schema/definition` | `defineSchema`, `defineTable`, relationship helpers |
742
+ | `@archlast/server/schema/validators` | `v` validator object |
743
+ | `@archlast/server/functions/definition` | `query`, `mutation`, `action`, `http`, `webhook`, `rpc` |
744
+ | `@archlast/server/functions/types` | Context types, function types |
745
+ | `@archlast/server/http` | HTTP handler utilities |
746
+ | `@archlast/server/webhook` | Webhook handler utilities |
747
+ | `@archlast/server/jobs` | `SchedulePreset`, job types |
748
+ | `@archlast/server/storage/types` | Storage type definitions |
749
+ | `@archlast/server/context` | Context type exports |
750
+ | `@archlast/server/di/*` | Dependency injection utilities |
751
+
752
+ ---
753
+
754
+ ## Environment Variables
755
+
756
+ Key variables used by the server runtime:
757
+
758
+ | Variable | Default | Description |
759
+ |----------|---------|-------------|
760
+ | `PORT` | `4000` | HTTP server port |
761
+ | `NODE_ENV` | `development` | Environment mode |
762
+ | `ARCHLAST_DB_ROOT` | `./data` | Database file directory |
763
+ | `ARCHLAST_ALLOWED_ORIGINS` | - | CORS allowed origins (CSV) |
764
+ | `ARCHLAST_CORS_ALLOW_CREDENTIALS` | `false` | Allow credentials in CORS |
765
+ | `STORAGE_ROOT` | `./storage` | Local file storage directory |
766
+ | `STORAGE_SIGNING_SECRET` | - | Secret for signed URLs |
767
+ | `S3_ENABLED` | `false` | Enable S3 storage |
768
+ | `S3_BUCKET` | - | S3 bucket name |
769
+ | `S3_REGION` | `us-east-1` | S3 region |
770
+ | `AWS_ACCESS_KEY_ID` | - | AWS access key |
771
+ | `AWS_SECRET_ACCESS_KEY` | - | AWS secret key |
772
+ | `ARCHLAST_AUTH_TOKEN_PEPPER` | - | Token signing pepper |
773
+ | `ARCHLAST_DASHBOARD_DIR` | - | Dashboard static files path |
774
+ | `ARCHLAST_DASHBOARD_URL` | - | Dashboard proxy URL |
775
+ | `ARCHLAST_STORE_PORT` | `7001` | Document store port |
776
+ | `ARCHLAST_STORE_HOST` | `127.0.0.1` | Document store host |
777
+ | `ARCHLAST_STORE_NO_TLS` | `false` | Disable TLS for document store |
778
+
779
+ ---
780
+
781
+ ## Docker Templates
782
+
783
+ Templates for Docker deployment are included in `templates/`:
784
+
785
+ - `docker-compose.yml` - Base compose file
786
+ - `docker-compose.dev.yml` - Development overrides
787
+ - `docker-compose.prod.yml` - Production configuration
788
+ - `.env.example` - Example environment variables
789
+ - `archlast.config.js` - CLI configuration template
790
+
791
+ The CLI uses these templates when running `archlast start`.
792
+
793
+ ---
794
+
795
+ ## Publishing (Maintainers)
31
796
 
32
797
  See `docs/npm-publishing.md` for release and publish steps.
798
+
799
+ ## License
800
+
801
+ MIT
package/docker/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Archlast Docker Assets
2
2
 
3
3
  This folder is reserved for Docker build assets distributed with the
4
- @archlast/server package. The production Dockerfile and service scripts
5
- are defined in the repository root under `docker/`.
4
+ `@archlast/server` package. The production Dockerfile and s6 service
5
+ definitions live in the repository root under `docker/`.
6
+
7
+ If you are packaging a runtime image, use the root `docker/Dockerfile` and the
8
+ service definitions in `docker/s6-rc.d/*`.
9
+
10
+ For local runtime via the CLI, use the templates bundled in
11
+ `packages/server/templates/`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archlast/server",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Archlast server library exports and Docker templates",
5
5
  "license": "MIT",
6
6
  "repository": {