@wenathlan/saddle 1.8.2 → 1.8.5
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 +9 -9
- package/browser/playwright.js +22 -0
- package/docs/.gitkeep +0 -0
- package/docs/gapmatrix.md +3 -3
- package/docs/libraryapi.md +1 -0
- package/docs/logs/.gitkeep +0 -0
- package/docs/packageaudit185.md +23 -0
- package/docs/plans/00.index.md +50 -0
- package/docs/plans/01.architecture.md +86 -0
- package/docs/plans/02.research.computer.use.md +58 -0
- package/docs/plans/03.research.captcha.bypass.md +68 -0
- package/docs/plans/04.research.sandbox.ai.md +52 -0
- package/docs/plans/05.capture.platform.md +57 -0
- package/docs/plans/06.dependencies.md +97 -0
- package/docs/plans/07.captcha.test.page.md +41 -0
- package/docs/plans/08.production.infra.md +70 -0
- package/docs/plans/09.database.schema.md +121 -0
- package/docs/plans/10.cloudinary.storage.md +57 -0
- package/docs/plans/11.movement.logs.json.md +72 -0
- package/docs/plans/12.research.atlas.agent.browser.md +79 -0
- package/docs/plans/13.research.anti.detection.md +898 -0
- package/docs/plans/14.research.proxy.md +1495 -0
- package/docs/plans/15.research.retry.rate.limit.md +1958 -0
- package/docs/plans/16.research.crawling.md +1417 -0
- package/docs/plans/17.research.caching.md +1610 -0
- package/docs/plans/18.research.content.extraction.md +1952 -0
- package/docs/plans/19.research.errors.events.md +1523 -0
- package/docs/plans/20.research.zod.validation.md +1350 -0
- package/docs/plans/21.research.batch.concurrency.md +1888 -0
- package/docs/plans/22.research.universal.runtime.md +944 -0
- package/docs/plans/23.research.ai.integration.md +1465 -0
- package/docs/plans/24.research.memory.persistence.md +1979 -0
- package/docs/plans/25.research.server.api.md +342 -0
- package/docs/plans/26.research.compilation.md +249 -0
- package/docs/plans/27.research.html.parsing.md +251 -0
- package/docs/plans/28.action.plan.md +50 -0
- package/docs/plans/29.api.reference.md +174 -0
- package/docs/plans/30.architecture.plan.md +94 -0
- package/docs/plans/31.auditoria.dados.md +163 -0
- package/docs/plans/32.bots.automacao.computacional.md +214 -0
- package/docs/plans/33.bots.codigo.revisao.md +220 -0
- package/docs/plans/34.bots.seguranca.cicd.md +366 -0
- package/docs/plans/35.comparativo.concorrencia.md +464 -0
- package/docs/plans/36.computational.memory.md +340 -0
- package/docs/plans/37.deploystrategy.md +394 -0
- package/docs/plans/38.flow.md +155 -0
- package/docs/plans/39.multi.platform.bot.md +252 -0
- package/docs/plans/40.npm.publish.md +250 -0
- package/docs/plans/41.o.que.falta.md +407 -0
- package/docs/plans/42.pesquisa.concorrencia.md +721 -0
- package/docs/plans/43.plan.universal.architecture.md +496 -0
- package/docs/plans/44.reference.md +100 -0
- package/docs/plans/45.robotarchitecture.md +237 -0
- package/docs/plans/46.scdnintegration.md +284 -0
- package/docs/plans/47.multiforge.readme.md +129 -0
- package/docs/plans/48.theory.v4.repo.os.md +152 -0
- package/docs/plans/49.third.party.infra.md +12 -0
- package/docs/plans/50.file.as.compute.md +39 -0
- package/docs/plans/51.architecture.virtual.processor.md +80 -0
- package/docs/plans/52.manifesto.v8.md +11 -0
- package/docs/plans/58.cdn.list.md +23 -0
- package/docs/plans/59.sql.frameworks.md +33 -0
- package/docs/plans/60.sql.thirdparty.md +26 -0
- package/docs/plans/61.objective.multiforge.md +63 -0
- package/docs/plans/62.huggingface.upload.md +26 -0
- package/docs/plans/63.kaggle.upload.md +24 -0
- package/docs/plans/64.npm.storage.md +30 -0
- package/docs/plans/65.rclone.terabox.md +32 -0
- package/docs/plans/66.buckets.and.models.todo.md +14 -0
- package/docs/plans/67.database.todo.md +13 -0
- package/docs/plans/68.deploy.packages.todo.md +12 -0
- package/docs/plans/69.report.human.operator.md +133 -0
- package/docs/plans/70.report.brain2qwerty.ems.md +135 -0
- package/docs/plans/71.report.hd.infinito.vram.md +155 -0
- package/docs/plans/72.plan.hd.infinito.node.md +146 -0
- package/docs/plans/73.plan.scifi.repos.md +125 -0
- package/docs/plans/74.000.manifesto.v8.flat.2..md +11 -0
- package/docs/plans/README.md +489 -0
- package/docs/plans/aggregate_platforms.mjs +146 -0
- package/docs/plans/examplesession.json +36 -0
- package/docs/plans/missing-facts.md +192 -0
- package/docs/plans/models.md +64 -0
- package/docs/plans/organize.cjs +270 -0
- package/docs/plans/platforms.md +2887 -0
- package/docs/plans/sites.md +31322 -0
- package/docs/platformpipelineaudit.md +18 -0
- package/docs/platformpipelines.md +13 -0
- package/docs/registryresearch.md +4 -0
- package/docs/release.md +4 -4
- package/docs/release182notes.md +2 -0
- package/docs/release184notes.md +7 -0
- package/docs/release185notes.md +7 -0
- package/docs/sources/farm.py +117 -0
- package/docs/sources/html/saddle1.html +132 -0
- package/docs/sources/html/saddle2.html +157 -0
- package/docs/sources/html/saddle3.html +119 -0
- package/docs/sources/html/saddle4.html +144 -0
- package/docs/sources/html/saddle5.html +72 -0
- package/docs/sources/html/saddle6.html +171 -0
- package/docs/sources/html/saddle7.html +236 -0
- package/docs/sources/saddle.ts +74 -0
- package/docs/sources/schema.prisma +88 -0
- package/docs/sources/script.sh +64 -0
- package/docs/sources/workflows.yml +458 -0
- package/docs/talks1/_body.txt +14 -0
- package/docs/talks1/_index.md +15 -0
- package/docs/talks1/_screenshot.png +0 -0
- package/docs/talks1/assistant-01.md +5 -0
- package/docs/talks1/assistant-02.md +5 -0
- package/docs/talks1/assistant-03.md +531 -0
- package/docs/talks1/assistant-04.md +26 -0
- package/docs/talks1/assistant-05.md +774 -0
- package/docs/talks1/assistant-06.md +1718 -0
- package/docs/talks1/scrape-share.cjs +185 -0
- package/docs/talks1/scrape-share.ts +183 -0
- package/docs/talks1/user-01.md +3 -0
- package/docs/talks1/user-02.md +3 -0
- package/docs/talks1/user-03.md +88 -0
- package/docs/talks1/user-04.md +3 -0
- package/docs/talks1/user-05.md +3 -0
- package/docs/talks1/user-06.md +88 -0
- package/docs/talks1/user-07.md +88 -0
- package/docs/talks2/_body.txt +14 -0
- package/docs/talks2/_index.md +16 -0
- package/docs/talks2/_screenshot.png +0 -0
- package/docs/talks2/assistant-01.md +5 -0
- package/docs/talks2/assistant-02.md +5 -0
- package/docs/talks2/assistant-03.md +424 -0
- package/docs/talks2/assistant-04.md +598 -0
- package/docs/talks2/assistant-05.md +1280 -0
- package/docs/talks2/assistant-06.md +1227 -0
- package/docs/talks2/assistant-07.md +1252 -0
- package/docs/talks2/user-01.md +3 -0
- package/docs/talks2/user-02.md +3 -0
- package/docs/talks2/user-03.md +88 -0
- package/docs/talks2/user-04.md +88 -0
- package/docs/talks2/user-05.md +88 -0
- package/docs/talks2/user-06.md +88 -0
- package/docs/talks2/user-07.md +3 -0
- package/docs/talks3/_body.txt +467 -0
- package/docs/talks3/_index.md +10 -0
- package/docs/talks3/_screenshot.png +0 -0
- package/docs/talks3/assistant-01.md +417 -0
- package/docs/talks3/assistant-02.md +417 -0
- package/docs/talks3/assistant-03.md +29 -0
- package/docs/talks3/assistant-04.md +727 -0
- package/docs/talks3/user-01.md +88 -0
- package/docs/talks3/user-02.md +88 -0
- package/docs/talks3/user-03.md +3 -0
- package/docs/talks3/user-04.md +3 -0
- package/docs/talks4/_body.txt +14 -0
- package/docs/talks4/_index.md +12 -0
- package/docs/talks4/_screenshot.png +0 -0
- package/docs/talks4/assistant-01.md +5 -0
- package/docs/talks4/assistant-02.md +5 -0
- package/docs/talks4/assistant-03.md +35 -0
- package/docs/talks4/assistant-04.md +512 -0
- package/docs/talks4/assistant-05.md +599 -0
- package/docs/talks4/user-01.md +3 -0
- package/docs/talks4/user-02.md +3 -0
- package/docs/talks4/user-03.md +88 -0
- package/docs/talks4/user-04.md +88 -0
- package/docs/talks4/user-05.md +7 -0
- package/docs/talks5/_body.txt +14 -0
- package/docs/talks5/_index.md +13 -0
- package/docs/talks5/_screenshot.png +0 -0
- package/docs/talks5/assistant-01.md +5 -0
- package/docs/talks5/assistant-02.md +5 -0
- package/docs/talks5/assistant-03.md +690 -0
- package/docs/talks5/assistant-04.md +758 -0
- package/docs/talks5/assistant-05.md +974 -0
- package/docs/talks5/user-01.md +3 -0
- package/docs/talks5/user-02.md +3 -0
- package/docs/talks5/user-03.md +105 -0
- package/docs/talks5/user-04.md +105 -0
- package/docs/talks5/user-05.md +63 -0
- package/docs/talks5/user-06.md +105 -0
- package/docs/talks6/_body.txt +14 -0
- package/docs/talks6/_index.md +9 -0
- package/docs/talks6/_screenshot.png +0 -0
- package/docs/talks6/assistant-01.md +5 -0
- package/docs/talks6/assistant-02.md +5 -0
- package/docs/talks6/assistant-03.md +1499 -0
- package/docs/talks6/user-01.md +3 -0
- package/docs/talks6/user-02.md +3 -0
- package/docs/talks6/user-03.md +88 -0
- package/docs/talks6/user-04.md +88 -0
- package/docs/talks7/_body.txt +14 -0
- package/docs/talks7/_index.md +10 -0
- package/docs/talks7/_screenshot.png +0 -0
- package/docs/talks7/assistant-01.md +5 -0
- package/docs/talks7/assistant-02.md +5 -0
- package/docs/talks7/assistant-03.md +523 -0
- package/docs/talks7/assistant-04.md +617 -0
- package/docs/talks7/user-01.md +3 -0
- package/docs/talks7/user-02.md +3 -0
- package/docs/talks7/user-03.md +105 -0
- package/docs/talks7/user-04.md +67 -0
- package/docs/talks8/conversa1.txt +1322 -0
- package/docs/talks8/conversa2.txt +237 -0
- package/docs/talks9/Beyond the Obvious_ 50 Plataformas Auto-Hospedadas de Forja de C/303/263digo para Al/303/251m de Gitea e GitLab.md" +174 -0
- package/docs/talks9/De NPM a Multi-Linguagem_ Uma Arquitetura T/303/251cnica para a Execu/303/247/303/243o Integrada de C/303/263digo no Ecossistema Node.js.md" +59 -0
- package/docs/talks9/De NPM a VMs Virtuais_ Uma An/303/241lise Arquitet/303/264nica para a Realiza/303/247/303/243o do Ciclo de Vida do Projeto SADDLE.md" +91 -0
- package/docs/talks9/Mapeamento da Engrenagem Computacional_ Uma Arquitetura para Execu/303/247/303/243o Isolada e Persist/303/252ncia em Ambientes Distribu/303/255dos.md" +116 -0
- package/docs/talks9/O Cen/303/241rio Pr/303/241tico do SADDLE_ Uma An/303/241lise de Viabilidade e Modelo de Ciclo de Vida Integrado.md" +128 -0
- package/docs/talks9/README (2).md +489 -0
- package/docs/talks9/README.md +198 -0
- package/docs/talks9/Viabilidade do Saddle_ Uma An/303/241lise T/303/251cnica da Transforma/303/247/303/243o de Armazenamento Remoto em Mem/303/263ria Computacional.md" +80 -0
- package/docs/talks9/conversa.txt +544 -0
- package/docs/talks9/other (2).md +39 -0
- package/docs/talks9/other.md +57 -0
- package/docs/talks9/outro.txt +24 -0
- package/extension/README.md +3 -3
- package/extension/serviceworker.js +63 -4
- package/extension/worker.js +3 -1
- package/package.json +12 -2
- package/packager/manifest.js +1 -1
- package/scrape/agent.ts +122 -0
- package/scrape/batch.ts +79 -0
- package/scrape/biome.json +76 -0
- package/scrape/browser.ts +222 -0
- package/scrape/cache.ts +84 -0
- package/scrape/chunking.ts +193 -0
- package/scrape/cli.ts +105 -0
- package/scrape/crawler.ts +115 -0
- package/scrape/dev-server.ts +94 -0
- package/scrape/errors.ts +132 -0
- package/scrape/events.ts +26 -0
- package/scrape/extract.ts +165 -0
- package/scrape/fetch.ts +105 -0
- package/scrape/formats.ts +85 -0
- package/scrape/headers.ts +71 -0
- package/scrape/index.ts +92 -0
- package/scrape/jsdom.d.ts +6 -0
- package/scrape/llms-txt.ts +84 -0
- package/scrape/middleware.ts +90 -0
- package/scrape/package-lock.json +9397 -0
- package/scrape/package.json +1420 -0
- package/scrape/pool.ts +95 -0
- package/scrape/port.ts +18 -0
- package/scrape/proxy.ts +103 -0
- package/scrape/rate-limiter.ts +95 -0
- package/scrape/renderer.ts +194 -0
- package/scrape/retry.ts +64 -0
- package/scrape/robots.ts +137 -0
- package/scrape/scrape.ts +123 -0
- package/scrape/serialize.ts +310 -0
- package/scrape/server.ts +137 -0
- package/scrape/session.ts +109 -0
- package/scrape/sitemap.ts +131 -0
- package/scrape/tokens.ts +45 -0
- package/scrape/tsconfig.json +28 -0
- package/scrape/types.ts +214 -0
- package/scrape/utils.ts +77 -0
- package/scrape/vite.config.ts +55 -0
- package/scrape/vitest.config.ts +17 -0
- package/workflow/templates.js +4 -4
|
@@ -0,0 +1,1499 @@
|
|
|
1
|
+
# assistant
|
|
2
|
+
|
|
3
|
+
[Thinking] The user is asking a very complex question about using memory/storage from various platforms (GitHub, GitLab, Gitea, Forgejo, HuggingFace, Kaggle, ModelScope, etc.) to create a sandbox container platform that can run Linux, Docker, with RAM and storage, using free infrastructure from these platforms.
|
|
4
|
+
They want me to research 40 searches and provide a step-by-step implementation plan. This is a very ambitious project - essentially they want to build a cloud sandbox platform using free resources from various platforms.
|
|
5
|
+
Let me break down the key concepts:
|
|
6
|
+
|
|
7
|
+
Using storage from repos/buckets as both storage and RAM (memory conversion)
|
|
8
|
+
|
|
9
|
+
Using CI/CD pipelines from GitHub, GitLab, Gitea, Forgejo to run sandboxes
|
|
10
|
+
|
|
11
|
+
Building on Docker, Linux, and container technology
|
|
12
|
+
|
|
13
|
+
Converting storage into RAM through virtual memory and memory emulation
|
|
14
|
+
|
|
15
|
+
Leveraging free npm packages, CDNs like jsdelivr and esm.sh
|
|
16
|
+
|
|
17
|
+
Integrating database ORMs like Drizzle, Prisma, and MySQL2
|
|
18
|
+
|
|
19
|
+
Deploying across platforms like Vercel, Netlify, HuggingFace Spaces, and Kaggle
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
This is extremely ambitious, so I need to research how to use CI/CD pipelines as compute resources, convert storage to memory, and leverage these free platforms effectively.
|
|
23
|
+
I'm also looking into container sandboxes, memory emulation, open source projects that could help, npm packages for virtual memory, and database ORMs as storage layers, plus infrastructure options like Docker and Linux on free tiers.
|
|
24
|
+
The user wants 40 searches with a todo list and full implementation, but I'm in plan mode right now, so I need to research and build a comprehensive plan first.
|
|
25
|
+
|
|
26
|
+
[Thinking] Now let me start the research. I'll use multiple search agents in parallel to gather information.
|
|
27
|
+
|
|
28
|
+
[Thinking] I now have comprehensive research on all 6 major topics. Let me update the todo list and compile the final implementation plan. Let me also search for a few more specific topics to fill in gaps.
|
|
29
|
+
|
|
30
|
+
[Thinking] Now I need to compile all the research into a comprehensive implementation plan. Let me organize this into a clear, actionable plan.
|
|
31
|
+
|
|
32
|
+
[Thinking] The research is complete and I've provided a comprehensive implementation plan. Let me give a concise summary to the user.
|
|
33
|
+
|
|
34
|
+
Entendi seu projeto ambicioso. Vou pesquisar e criar um plano detalhado para essa plataforma de sandbox usando infraestrutura gratuita. Deixe-me fazer as buscas necessárias.
|
|
35
|
+
|
|
36
|
+
Iniciando as pesquisas. Vou buscar informações sobre todas as tecnologias e abordagens relevantes.
|
|
37
|
+
|
|
38
|
+
Excelente! As pesquisas estão completas. Vou agora compilar o plano completo de implementação.
|
|
39
|
+
|
|
40
|
+
PLANO COMPLETO: Plataforma de Sandbox com Infraestrutura Gratuita
|
|
41
|
+
VISÃO GERAL DO SISTEMA
|
|
42
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
43
|
+
│ ZAI SANDBOX PLATFORM │
|
|
44
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
45
|
+
│ │
|
|
46
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|
47
|
+
│ │ CONTROL │ │ COMPUTE │ │ STORAGE/MEMORY │ │
|
|
48
|
+
│ │ PLANE │ │ LAYER │ │ LAYER │ │
|
|
49
|
+
│ │ │ │ │ │ │ │
|
|
50
|
+
│ │ Cloudflare │ │ GitHub │ │ Cloudflare R2/KV │ │
|
|
51
|
+
│ │ Workers │ │ Actions │ │ + tmpfs/zram │ │
|
|
52
|
+
│ │ (orchestra- │ │ (4CPU/16GB) │ │ + SQLite/libSQL │ │
|
|
53
|
+
│ │ tor) │ │ │ │ + memfs/mmap │ │
|
|
54
|
+
│ │ │ │ GitLab CI │ │ + GitHub Artifacts │ │
|
|
55
|
+
│ │ Vercel/ │ │ (2CPU/8GB) │ │ + HuggingFace │ │
|
|
56
|
+
│ │ Netlify │ │ │ │ + Kaggle datasets │ │
|
|
57
|
+
│ │ (API routes)│ │ Oracle Cloud│ │ + npm CDN │ │
|
|
58
|
+
│ │ │ │ (2OCPU/12GB)│ │ (jsDelivr/esm.sh) │ │
|
|
59
|
+
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
|
|
60
|
+
│ │
|
|
61
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
62
|
+
│ │ SANDBOX ISOLATION LAYERS │ │
|
|
63
|
+
│ │ gVisor (user-space kernel) + cgroups v2 + seccomp │ │
|
|
64
|
+
│ │ + AppArmor + OverlayFS + Podman rootless │ │
|
|
65
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
66
|
+
│ │
|
|
67
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
68
|
+
│ │ MEMORY HYBRID LAYER │ │
|
|
69
|
+
│ │ RAM (real) + zram (compressed) + swap + tmpfs │ │
|
|
70
|
+
│ │ + mmap (storage->memory bridge) + SQLite VFS │ │
|
|
71
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
72
|
+
│ │
|
|
73
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
74
|
+
|
|
75
|
+
FASE 1: ESTRUTURA DO PROJETO
|
|
76
|
+
1.1 Layout do Repositório
|
|
77
|
+
zai-sandbox/
|
|
78
|
+
├── packages/
|
|
79
|
+
│ ├── core/ # Core sandbox engine (TypeScript)
|
|
80
|
+
│ │ ├── src/
|
|
81
|
+
│ │ │ ├── sandbox/
|
|
82
|
+
│ │ │ │ ├── manager.ts # Sandbox lifecycle manager
|
|
83
|
+
│ │ │ │ ├── isolate.ts # gVisor/process isolation
|
|
84
|
+
│ │ │ │ ├── resources.ts # cgroups v2 resource limits
|
|
85
|
+
│ │ │ │ └── snapshot.ts # Snapshot/restore (Firecracker style)
|
|
86
|
+
│ │ │ ├── memory/
|
|
87
|
+
│ │ │ │ ├── hybrid.ts # RAM + zram + swap + tmpfs bridge
|
|
88
|
+
│ │ │ │ ├── mmap-bridge.ts # mmap storage->memory conversion
|
|
89
|
+
│ │ │ │ ├── vfs.ts # Virtual filesystem (memfs)
|
|
90
|
+
│ │ │ │ └── sqlite-vfs.ts # SQLite-backed persistent VFS
|
|
91
|
+
│ │ │ ├── storage/
|
|
92
|
+
│ │ │ │ ├── r2.ts # Cloudflare R2 object storage
|
|
93
|
+
│ │ │ │ ├── github-artifacts.ts
|
|
94
|
+
│ │ │ │ ├── huggingface.ts # HuggingFace datasets
|
|
95
|
+
│ │ │ │ └── npm-cdn.ts # jsDelivr/esm.sh loader
|
|
96
|
+
│ │ │ ├── orchestrator/
|
|
97
|
+
│ │ │ │ ├── github-actions.ts
|
|
98
|
+
│ │ │ │ ├── gitlab-ci.ts
|
|
99
|
+
│ │ │ │ ├── forgejo.ts
|
|
100
|
+
│ │ │ │ └── gitea.ts
|
|
101
|
+
│ │ │ ├── db/
|
|
102
|
+
│ │ │ │ ├── schema.ts # Drizzle ORM schema
|
|
103
|
+
│ │ │ │ ├── turso.ts # libSQL/Turso client
|
|
104
|
+
│ │ │ │ └── migrations/
|
|
105
|
+
│ │ │ └── index.ts
|
|
106
|
+
│ │ ├── package.json
|
|
107
|
+
│ │ └── tsconfig.json
|
|
108
|
+
│ ├── worker/ # Sandbox worker process
|
|
109
|
+
│ │ ├── src/
|
|
110
|
+
│ │ │ ├── runner.ts # Code execution runner
|
|
111
|
+
│ │ │ ├── shell.ts # Linux shell emulation
|
|
112
|
+
│ │ │ └── network.ts # Network isolation
|
|
113
|
+
│ │ └── package.json
|
|
114
|
+
│ └── web/ # Web interface
|
|
115
|
+
│ ├── src/
|
|
116
|
+
│ │ ├── app/
|
|
117
|
+
│ │ ├── components/
|
|
118
|
+
│ │ └── lib/
|
|
119
|
+
│ └── package.json
|
|
120
|
+
├── infra/
|
|
121
|
+
│ ├── docker/
|
|
122
|
+
│ │ ├── Dockerfile.worker
|
|
123
|
+
│ │ ├── Dockerfile.gvisor
|
|
124
|
+
│ │ └── docker-compose.yml
|
|
125
|
+
│ ├── k8s/
|
|
126
|
+
│ │ ├── sandbox-pod.yaml
|
|
127
|
+
│ │ ├── gvisor-runtimeclass.yaml
|
|
128
|
+
│ │ └── resource-quota.yaml
|
|
129
|
+
│ └── scripts/
|
|
130
|
+
│ ├── setup-gvisor.sh
|
|
131
|
+
│ ├── setup-cgroups.sh
|
|
132
|
+
│ └── setup-zram.sh
|
|
133
|
+
├── .github/
|
|
134
|
+
│ └── workflows/
|
|
135
|
+
│ ├── sandbox-compute.yml # GitHub Actions como compute
|
|
136
|
+
│ ├── build-deploy.yml
|
|
137
|
+
│ └── cron-sandbox.yml
|
|
138
|
+
├── .gitlab-ci.yml
|
|
139
|
+
├── .forgejo/workflows/
|
|
140
|
+
│ └── sandbox.yml
|
|
141
|
+
├── package.json # Monorepo root
|
|
142
|
+
├── turbo.json
|
|
143
|
+
└── README.md
|
|
144
|
+
|
|
145
|
+
FASE 2: MEMÓRIA HIBRIDA (Storage ↔ RAM)
|
|
146
|
+
2.1 Configuração do Sistema (Linux)
|
|
147
|
+
# === /infra/scripts/setup-zram.sh ===
|
|
148
|
+
#!/bin/bash
|
|
149
|
+
|
|
150
|
+
# Criar zram para RAM comprimida (50% da RAM real = ~2x efetiva)
|
|
151
|
+
modprobe zram num_devices=1
|
|
152
|
+
zramctl /dev/zram0 --algorithm zstd --size 8G
|
|
153
|
+
mkswap -U clear /dev/zram0
|
|
154
|
+
swapon --discard --priority 100 /dev/zram0
|
|
155
|
+
|
|
156
|
+
# Configurar tmpfs para /tmp (armazenamento rápido = "RAM virtual")
|
|
157
|
+
mount -t tmpfs -o size=4G,mode=1777,noexec,nosuid,nodev tmpfs /tmp
|
|
158
|
+
|
|
159
|
+
# Configurar sysctl para otimizar swap
|
|
160
|
+
cat >> /etc/sysctl.d/99-zai-memory.conf << 'EOF'
|
|
161
|
+
vm.swappiness=180
|
|
162
|
+
vm.watermark_boost_factor=0
|
|
163
|
+
vm.watermark_scale_factor=125
|
|
164
|
+
vm.page-cluster=0
|
|
165
|
+
vm.overcommit_memory=1
|
|
166
|
+
EOF
|
|
167
|
+
sysctl -p /etc/sysctl.d/99-zai-memory.conf
|
|
168
|
+
|
|
169
|
+
echo "Memory bridge configurado: zram(8G) + tmpfs(4G)"
|
|
170
|
+
2.2 Bridge Storage→RAM (Node.js)
|
|
171
|
+
// packages/core/src/memory/hybrid.ts
|
|
172
|
+
|
|
173
|
+
import { Volume } from 'memfs';
|
|
174
|
+
import { createClient } from '@libsql/client';
|
|
175
|
+
import * as mmap from 'mmap-io';
|
|
176
|
+
import * as fs from 'fs';
|
|
177
|
+
|
|
178
|
+
interface MemoryTier {
|
|
179
|
+
name: string;
|
|
180
|
+
type: 'ram' | 'zram' | 'tmpfs' | 'mmap' | 'sqlite' | 'r2';
|
|
181
|
+
size: number;
|
|
182
|
+
speed: number; // ns per access
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
interface SandboxMemory {
|
|
186
|
+
// Tier 1: RAM real (microseconds)
|
|
187
|
+
ram: Volume;
|
|
188
|
+
|
|
189
|
+
// Tier 2: zram comprimido (RAM*2 efetiva)
|
|
190
|
+
zram: string; // /dev/zram0 mount point
|
|
191
|
+
|
|
192
|
+
// Tier 3: tmpfs no disco (memória virtual)
|
|
193
|
+
tmpfs: string; // /tmp mount point
|
|
194
|
+
|
|
195
|
+
// Tier 4: mmap storage→memory bridge
|
|
196
|
+
mmapBridge: Map<string, mmap.MappedBuffer>;
|
|
197
|
+
|
|
198
|
+
// Tier 5: SQLite VFS (persistente)
|
|
199
|
+
sqliteVfs: any;
|
|
200
|
+
|
|
201
|
+
// Tier 6: Object storage (R2/Artifacts)
|
|
202
|
+
remoteStorage: any;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export class HybridMemoryManager {
|
|
206
|
+
private tiers: MemoryTier[] = [
|
|
207
|
+
{ name: 'ram', type: 'ram', size: 16 * 1024 * 1024 * 1024, speed: 100 },
|
|
208
|
+
{ name: 'zram', type: 'zram', size: 8 * 1024 * 1024 * 1024, speed: 500 },
|
|
209
|
+
{ name: 'tmpfs', type: 'tmpfs', size: 4 * 1024 * 1024 * 1024, speed: 1000 },
|
|
210
|
+
{ name: 'mmap', type: 'mmap', size: 50 * 1024 * 1024 * 1024, speed: 5000 },
|
|
211
|
+
{ name: 'sqlite', type: 'sqlite', size: 100 * 1024 * 1024 * 1024, speed: 10000 },
|
|
212
|
+
{ name: 'r2', type: 'r2', size: 10 * 1024 * 1024 * 1024, speed: 50000 },
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
private memfsVolume = Volume.fromJSON({});
|
|
216
|
+
private mmapBuffers = new Map<string, any>();
|
|
217
|
+
|
|
218
|
+
async init(): Promise<void> {
|
|
219
|
+
// Mount tmpfs se disponível
|
|
220
|
+
try {
|
|
221
|
+
if (!fs.existsSync('/tmp/zai-sandbox')) {
|
|
222
|
+
fs.mkdirSync('/tmp/zai-sandbox', { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.warn('tmpfs não disponível, usando RAM apenas');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Bridge: converte arquivo de storage em RAM via mmap
|
|
230
|
+
async bridgeToMemory(filePath: string): Promise<Buffer> {
|
|
231
|
+
const stat = fs.statSync(filePath);
|
|
232
|
+
const fd = fs.openSync(filePath, 'r');
|
|
233
|
+
|
|
234
|
+
// mmap converte disco → memória mapeada
|
|
235
|
+
const buffer = mmap.map(
|
|
236
|
+
stat.size,
|
|
237
|
+
mmap.PROT_READ | mmap.PROT_WRITE,
|
|
238
|
+
mmap.MAP_SHARED,
|
|
239
|
+
fd,
|
|
240
|
+
0
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
this.mmapBuffers.set(filePath, { buffer, fd, size: stat.size });
|
|
244
|
+
return buffer;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Bridge: converte dados em RAM para persistência SQLite
|
|
248
|
+
async persistToSQLite(key: string, data: Buffer): Promise<void> {
|
|
249
|
+
const db = await createClient({ url: ':memory:' });
|
|
250
|
+
await db.execute(`
|
|
251
|
+
CREATE TABLE IF NOT EXISTS sandbox_files (
|
|
252
|
+
key TEXT PRIMARY KEY,
|
|
253
|
+
data BLOB,
|
|
254
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
255
|
+
)
|
|
256
|
+
`);
|
|
257
|
+
await db.execute({
|
|
258
|
+
sql: 'INSERT OR REPLACE INTO sandbox_files (key, data) VALUES (?, ?)',
|
|
259
|
+
args: [key, data]
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Bridge: converte SQLite → RAM (carrega em memfs)
|
|
264
|
+
async loadFromSQLite(key: string): Promise<Buffer | null> {
|
|
265
|
+
const db = await createClient({ url: ':memory:' });
|
|
266
|
+
const result = await db.execute({
|
|
267
|
+
sql: 'SELECT data FROM sandbox_files WHERE key = ?',
|
|
268
|
+
args: [key]
|
|
269
|
+
});
|
|
270
|
+
if (result.rows.length > 0) {
|
|
271
|
+
return result.rows[0].data as Buffer;
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Bridge: converte npm package do CDN em memória
|
|
277
|
+
async loadFromCDN(packageName: string, version: string): Promise<string> {
|
|
278
|
+
const url = `https://esm.sh/${packageName}@${version}`;
|
|
279
|
+
const response = await fetch(url);
|
|
280
|
+
return await response.text();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Bridge: converte GitHub artifact em memória
|
|
284
|
+
async loadFromArtifact(owner: string, repo: string, artifactId: number): Promise<Buffer> {
|
|
285
|
+
const token = process.env.GITHUB_TOKEN;
|
|
286
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/actions/artifacts/${artifactId}/zip`;
|
|
287
|
+
const response = await fetch(url, {
|
|
288
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
289
|
+
});
|
|
290
|
+
return Buffer.from(await response.arrayBuffer());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Bridge: converte HuggingFace dataset em memória
|
|
294
|
+
async loadFromHuggingFace(datasetId: string, filename: string): Promise<Buffer> {
|
|
295
|
+
const url = `https://huggingface.co/datasets/${datasetId}/resolve/main/${filename}`;
|
|
296
|
+
const response = await fetch(url);
|
|
297
|
+
return Buffer.from(await response.arrayBuffer());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Auto-escalonamento entre tiers
|
|
301
|
+
async autoEscalate(key: string, size: number): Promise<void> {
|
|
302
|
+
// Se cabe em RAM, usa memfs
|
|
303
|
+
if (size < 64 * 1024 * 1024) { // < 64MB
|
|
304
|
+
this.memfsVolume.writeFileSync(key, await this.loadFromSQLite(key) || Buffer.alloc(0));
|
|
305
|
+
}
|
|
306
|
+
// Se não, usa mmap
|
|
307
|
+
else if (size < 1024 * 1024 * 1024) { // < 1GB
|
|
308
|
+
const tempPath = `/tmp/zai-sandbox/${key}`;
|
|
309
|
+
fs.writeFileSync(tempPath, await this.loadFromSQLite(key) || Buffer.alloc(0));
|
|
310
|
+
await this.bridgeToMemory(tempPath);
|
|
311
|
+
}
|
|
312
|
+
// Se muito grande, mantém no SQLite/R2
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getStats(): any {
|
|
316
|
+
return {
|
|
317
|
+
memfs: this.memfsVolume.toJSON(),
|
|
318
|
+
mmapBuffers: Array.from(this.mmapBuffers.keys()),
|
|
319
|
+
tiers: this.tiers,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
FASE 3: SANDBOX COM CI/CD COMPUTE
|
|
325
|
+
3.1 GitHub Actions como Compute (o mais generoso)
|
|
326
|
+
# .github/workflows/sandbox-compute.yml
|
|
327
|
+
name: ZAI Sandbox Compute
|
|
328
|
+
on:
|
|
329
|
+
workflow_dispatch:
|
|
330
|
+
inputs:
|
|
331
|
+
sandbox_id:
|
|
332
|
+
description: 'Sandbox ID to run'
|
|
333
|
+
required: true
|
|
334
|
+
cpu:
|
|
335
|
+
description: 'CPU cores (1-4)'
|
|
336
|
+
default: '4'
|
|
337
|
+
ram_gb:
|
|
338
|
+
description: 'RAM in GB (1-16)'
|
|
339
|
+
default: '16'
|
|
340
|
+
code:
|
|
341
|
+
description: 'Code to execute'
|
|
342
|
+
required: true
|
|
343
|
+
|
|
344
|
+
jobs:
|
|
345
|
+
sandbox:
|
|
346
|
+
runs-on: ubuntu-latest # 4 CPU, 16 GB RAM (public repo = ilimitado)
|
|
347
|
+
timeout-minutes: 350 # ~6 horas máximo
|
|
348
|
+
|
|
349
|
+
container:
|
|
350
|
+
image: docker:latest
|
|
351
|
+
options: --cpus=${{ inputs.cpu }} --memory=${{ inputs.ram_gb }}g
|
|
352
|
+
|
|
353
|
+
services:
|
|
354
|
+
docker:
|
|
355
|
+
image: docker:dind
|
|
356
|
+
options: --privileged
|
|
357
|
+
|
|
358
|
+
steps:
|
|
359
|
+
- uses: actions/checkout@v4
|
|
360
|
+
|
|
361
|
+
- name: Setup gVisor sandbox
|
|
362
|
+
run: |
|
|
363
|
+
# Instalar gVisor para isolamento
|
|
364
|
+
curl -fsSL https://gvisor.dev/archive.key | gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
|
|
365
|
+
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" > /etc/apt/sources.list.d/gvisor.list
|
|
366
|
+
apt-get update && apt-get install -y runsc
|
|
367
|
+
|
|
368
|
+
- name: Setup memory bridge
|
|
369
|
+
run: |
|
|
370
|
+
# Criar zram (RAM comprimida)
|
|
371
|
+
modprobe zram
|
|
372
|
+
echo zstd > /sys/block/zram0/comp_algorithm
|
|
373
|
+
echo 8G > /sys/block/zram0/disksize
|
|
374
|
+
mkswap /dev/zram0
|
|
375
|
+
swapon /dev/zram0
|
|
376
|
+
|
|
377
|
+
# Criar tmpfs
|
|
378
|
+
mount -t tmpfs -o size=4G tmpfs /tmp/sandbox
|
|
379
|
+
|
|
380
|
+
- name: Execute sandbox code
|
|
381
|
+
run: |
|
|
382
|
+
# Rodar código no gVisor sandbox com cgroups
|
|
383
|
+
docker run --rm \
|
|
384
|
+
--runtime=runsc \
|
|
385
|
+
--cpus=${{ inputs.cpu }} \
|
|
386
|
+
--memory=${{ inputs.ram_gb }}g \
|
|
387
|
+
--pids-limit=512 \
|
|
388
|
+
--read-only \
|
|
389
|
+
--tmpfs /tmp:size=2G \
|
|
390
|
+
--volume /tmp/sandbox:/workspace \
|
|
391
|
+
ubuntu:24.04 \
|
|
392
|
+
bash -c "
|
|
393
|
+
apt-get update && apt-get install -y nodejs npm &&
|
|
394
|
+
echo '${{ inputs.code }}' > /workspace/code.js &&
|
|
395
|
+
node /workspace/code.js
|
|
396
|
+
"
|
|
397
|
+
|
|
398
|
+
- name: Upload results to R2
|
|
399
|
+
run: |
|
|
400
|
+
# Salvar resultados no Cloudflare R2
|
|
401
|
+
npx wrangler r2 object put sandbox-results/${{ inputs.sandbox_id }}/output.json \
|
|
402
|
+
--file /tmp/sandbox/output.json \
|
|
403
|
+
--remote
|
|
404
|
+
|
|
405
|
+
- name: Upload artifacts
|
|
406
|
+
uses: actions/upload-artifact@v4
|
|
407
|
+
with:
|
|
408
|
+
name: sandbox-${{ inputs.sandbox_id }}
|
|
409
|
+
path: /tmp/sandbox/
|
|
410
|
+
retention-days: 7
|
|
411
|
+
3.2 GitLab CI como Compute
|
|
412
|
+
# .gitlab-ci.yml
|
|
413
|
+
variables:
|
|
414
|
+
SANDBOX_ID: "manual"
|
|
415
|
+
CPU_LIMIT: "2"
|
|
416
|
+
RAM_LIMIT: "8g"
|
|
417
|
+
|
|
418
|
+
stages:
|
|
419
|
+
- sandbox
|
|
420
|
+
|
|
421
|
+
sandbox-run:
|
|
422
|
+
stage: sandbox
|
|
423
|
+
image: docker:latest
|
|
424
|
+
tags:
|
|
425
|
+
- saas-linux-small-amd64 # 2 CPU, 8 GB RAM
|
|
426
|
+
services:
|
|
427
|
+
- docker:dind
|
|
428
|
+
variables:
|
|
429
|
+
DOCKER_TLS_CERTDIR: "/certs"
|
|
430
|
+
script:
|
|
431
|
+
- |
|
|
432
|
+
# Setup gVisor
|
|
433
|
+
curl -fsSL https://gvisor.dev/archive.key | gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
|
|
434
|
+
apt-get update && apt-get install -y runsc
|
|
435
|
+
|
|
436
|
+
# Criar sandbox com isolamento
|
|
437
|
+
docker run --rm \
|
|
438
|
+
--runtime=runsc \
|
|
439
|
+
--cpus=${CPU_LIMIT} \
|
|
440
|
+
--memory=${RAM_LIMIT} \
|
|
441
|
+
--read-only \
|
|
442
|
+
--tmpfs /tmp:rw,size=2G \
|
|
443
|
+
ubuntu:24.04 \
|
|
444
|
+
bash -c "
|
|
445
|
+
apt-get update && apt-get install -y nodejs npm &&
|
|
446
|
+
node -e '${SANDBOX_CODE}'
|
|
447
|
+
"
|
|
448
|
+
|
|
449
|
+
# Salvar estado no Cloudflare R2
|
|
450
|
+
- npx wrangler r2 object put sandbox/${SANDBOX_ID}/state.tar \
|
|
451
|
+
--file state.tar --remote
|
|
452
|
+
|
|
453
|
+
rules:
|
|
454
|
+
- if: $CI_PIPELINE_SOURCE == "api"
|
|
455
|
+
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
456
|
+
3.3 Oracle Cloud Always Free (2 OCPU ARM + 12GB RAM)
|
|
457
|
+
# === Infraestrutura permanente no Oracle Cloud ===
|
|
458
|
+
# Criar instância Always Free: VM.Standard.A1.Flex (2 OCPU, 12 GB RAM)
|
|
459
|
+
|
|
460
|
+
# Instalar dependências
|
|
461
|
+
apt-get update && apt-get install -y docker.io podman gvisor
|
|
462
|
+
|
|
463
|
+
# Instalar gVisor
|
|
464
|
+
curl -fsSL https://gvisor.dev/archive.key | gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
|
|
465
|
+
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" > /etc/apt/sources.list.d/gvisor.list
|
|
466
|
+
apt-get update && apt-get install -y runsc
|
|
467
|
+
|
|
468
|
+
# Configurar zram (RAM comprimida)
|
|
469
|
+
modprobe zram
|
|
470
|
+
echo zstd > /sys/block/zram0/comp_algorithm
|
|
471
|
+
echo 6G > /sys/block/zram0/disksize
|
|
472
|
+
mkswap /dev/zram0
|
|
473
|
+
swapon /dev/zram0
|
|
474
|
+
|
|
475
|
+
# Configurar tmpfs
|
|
476
|
+
mount -t tmpfs -o size=4G tmpfs /tmp/sandbox
|
|
477
|
+
|
|
478
|
+
# Instalar e configurar Gitea com Actions Runner
|
|
479
|
+
docker run -d \
|
|
480
|
+
--name gitea \
|
|
481
|
+
-p 3000:3000 -p 2222:22 \
|
|
482
|
+
-v gitea-data:/data \
|
|
483
|
+
-v /etc/timezone:/etc/timezone:ro \
|
|
484
|
+
-v /etc/localtime:/etc/localtime:ro \
|
|
485
|
+
gitea/gitea:latest
|
|
486
|
+
|
|
487
|
+
# Instalar Gitea Actions Runner
|
|
488
|
+
docker run -d \
|
|
489
|
+
--name gitea-runner \
|
|
490
|
+
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
491
|
+
-e GITEA_INSTANCE_URL=http://gitea:3000 \
|
|
492
|
+
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
|
|
493
|
+
gitea/act_runner:latest
|
|
494
|
+
|
|
495
|
+
FASE 4: ISOLAMENTO DO SANDBOX
|
|
496
|
+
4.1 gVisor (User-Space Kernel) - Sem KVM necessário
|
|
497
|
+
// packages/core/src/sandbox/isolate.ts
|
|
498
|
+
|
|
499
|
+
import { execSync, spawn } from 'child_process';
|
|
500
|
+
import * as fs from 'fs';
|
|
501
|
+
|
|
502
|
+
interface SandboxConfig {
|
|
503
|
+
id: string;
|
|
504
|
+
cpu: number;
|
|
505
|
+
ramGb: number;
|
|
506
|
+
diskGb: number;
|
|
507
|
+
network: boolean;
|
|
508
|
+
code: string;
|
|
509
|
+
language: 'javascript' | 'python' | 'bash';
|
|
510
|
+
timeout: number; // ms
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
interface SandboxResult {
|
|
514
|
+
stdout: string;
|
|
515
|
+
stderr: string;
|
|
516
|
+
exitCode: number;
|
|
517
|
+
memoryUsed: number;
|
|
518
|
+
duration: number;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export class GVisorIsolate {
|
|
522
|
+
private static readonly RUNSC = '/usr/local/bin/runsc';
|
|
523
|
+
|
|
524
|
+
async create(config: SandboxConfig): Promise<string> {
|
|
525
|
+
const containerId = `sandbox-${config.id}`;
|
|
526
|
+
|
|
527
|
+
// Criar rootfs snapshot
|
|
528
|
+
const rootfs = `/tmp/sandbox-rootfs/${config.id}`;
|
|
529
|
+
fs.mkdirSync(rootfs, { recursive: true });
|
|
530
|
+
|
|
531
|
+
// Copiar base image
|
|
532
|
+
execSync(`cp -r /var/lib/gvisor/rootfs/* ${rootfs}/`);
|
|
533
|
+
|
|
534
|
+
// Preparar workspace
|
|
535
|
+
const workspace = `/tmp/sandbox-workspace/${config.id}`;
|
|
536
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
537
|
+
fs.writeFileSync(`${workspace}/code.${this.getExtension(config.language)}`, config.code);
|
|
538
|
+
|
|
539
|
+
return containerId;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async run(config: SandboxConfig): Promise<SandboxResult> {
|
|
543
|
+
const containerId = `sandbox-${config.id}`;
|
|
544
|
+
const workspace = `/tmp/sandbox-workspace/${config.id}`;
|
|
545
|
+
const startTime = Date.now();
|
|
546
|
+
|
|
547
|
+
// Rodar com gVisor (isolamento em user-space, sem KVM)
|
|
548
|
+
const cmd = [
|
|
549
|
+
GVisorIsolate.RUNSC,
|
|
550
|
+
'run',
|
|
551
|
+
'--platform=systrap', // user-space platform
|
|
552
|
+
`--cpu=${config.cpu}`,
|
|
553
|
+
`--ram=${config.ramGb}G`,
|
|
554
|
+
'--network=none', // Sem rede por padrão
|
|
555
|
+
'--overlay2', // OverlayFS para CoW
|
|
556
|
+
`--rootfs=/var/lib/gvisor/rootfs/${config.id}`,
|
|
557
|
+
containerId,
|
|
558
|
+
'bash', '-c',
|
|
559
|
+
`cd /workspace && ${this.getCommand(config)}`
|
|
560
|
+
].join(' ');
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
const output = execSync(cmd, {
|
|
564
|
+
timeout: config.timeout,
|
|
565
|
+
encoding: 'utf-8',
|
|
566
|
+
env: {
|
|
567
|
+
...process.env,
|
|
568
|
+
NODE_PATH: '/usr/lib/node_modules',
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
return {
|
|
573
|
+
stdout: output,
|
|
574
|
+
stderr: '',
|
|
575
|
+
exitCode: 0,
|
|
576
|
+
memoryUsed: this.getMemoryUsage(containerId),
|
|
577
|
+
duration: Date.now() - startTime,
|
|
578
|
+
};
|
|
579
|
+
} catch (error: any) {
|
|
580
|
+
return {
|
|
581
|
+
stdout: error.stdout || '',
|
|
582
|
+
stderr: error.stderr || error.message,
|
|
583
|
+
exitCode: error.status || 1,
|
|
584
|
+
memoryUsed: this.getMemoryUsage(containerId),
|
|
585
|
+
duration: Date.now() - startTime,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async destroy(containerId: string): Promise<void> {
|
|
591
|
+
try {
|
|
592
|
+
execSync(`${GVisorIsolate.RUNSC} delete --force ${containerId}`);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
// Ignorar se já destruído
|
|
595
|
+
}
|
|
596
|
+
// Limpar workspace
|
|
597
|
+
const id = containerId.replace('sandbox-', '');
|
|
598
|
+
fs.rmSync(`/tmp/sandbox-workspace/${id}`, { recursive: true, force: true });
|
|
599
|
+
fs.rmSync(`/tmp/sandbox-rootfs/${id}`, { recursive: true, force: true });
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private getMemoryUsage(containerId: string): number {
|
|
603
|
+
try {
|
|
604
|
+
const cgroupPath = `/sys/fs/cgroup/system.slice/${containerId}/memory.current`;
|
|
605
|
+
if (fs.existsSync(cgroupPath)) {
|
|
606
|
+
return parseInt(fs.readFileSync(cgroupPath, 'utf-8'));
|
|
607
|
+
}
|
|
608
|
+
} catch (e) {}
|
|
609
|
+
return 0;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private getExtension(lang: string): string {
|
|
613
|
+
const exts: Record<string, string> = { javascript: 'js', python: 'py', bash: 'sh' };
|
|
614
|
+
return exts[lang] || 'txt';
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private getCommand(config: SandboxConfig): string {
|
|
618
|
+
const cmds: Record<string, string> = {
|
|
619
|
+
javascript: `node code.js`,
|
|
620
|
+
python: `python3 code.py`,
|
|
621
|
+
bash: `bash code.sh`,
|
|
622
|
+
};
|
|
623
|
+
return cmds[config.language] || `cat code.*`;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
4.2 cgroups v2 para Limitação de Recursos
|
|
627
|
+
// packages/core/src/sandbox/resources.ts
|
|
628
|
+
|
|
629
|
+
import * as fs from 'fs';
|
|
630
|
+
import { execSync } from 'child_process';
|
|
631
|
+
|
|
632
|
+
interface ResourceLimits {
|
|
633
|
+
cpuQuota: string; // Ex: "400000 100000" = 4 cores
|
|
634
|
+
memoryMax: string; // Ex: "8589934592" = 8GB
|
|
635
|
+
memoryHigh: string; // Ex: "6442450944" = 6GB
|
|
636
|
+
pidsMax: number; // Ex: 512
|
|
637
|
+
ioMax: string; // Ex: "8:0 rbps=104857600 wbps=104857600"
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export class CGroupsManager {
|
|
641
|
+
private cgroupPath: string;
|
|
642
|
+
|
|
643
|
+
constructor(sandboxId: string) {
|
|
644
|
+
this.cgroupPath = `/sys/fs/cgroup/zai-sandbox-${sandboxId}`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async setup(limits: ResourceLimits): Promise<void> {
|
|
648
|
+
// Criar cgroup
|
|
649
|
+
fs.mkdirSync(this.cgroupPath, { recursive: true });
|
|
650
|
+
|
|
651
|
+
// Habilitar controladores
|
|
652
|
+
fs.writeFileSync(`${this.cgroupPath}/cgroup.subtree_control`, '+memory +cpu +pids +io');
|
|
653
|
+
|
|
654
|
+
// Configurar limites de memória
|
|
655
|
+
fs.writeFileSync(`${this.cgroupPath}/memory.max`, limits.memoryMax);
|
|
656
|
+
fs.writeFileSync(`${this.cgroupPath}/memory.high`, limits.memoryHigh);
|
|
657
|
+
fs.writeFileSync(`${this.cgroupPath}/memory.swap.max`, '0'); // Sem swap
|
|
658
|
+
|
|
659
|
+
// Configurar limites de CPU
|
|
660
|
+
fs.writeFileSync(`${this.cgroupPath}/cpu.max`, limits.cpuQuota);
|
|
661
|
+
|
|
662
|
+
// Configurar limites de processos
|
|
663
|
+
fs.writeFileSync(`${this.cgroupPath}/pids.max`, limits.pidsMax.toString());
|
|
664
|
+
|
|
665
|
+
// Configurar limites de I/O
|
|
666
|
+
fs.writeFileSync(`${this.cgroupPath}/io.max`, limits.ioMax);
|
|
667
|
+
|
|
668
|
+
// Matar processos se exceder OOM
|
|
669
|
+
fs.writeFileSync(`${this.cgroupPath}/memory.oom.group`, '1');
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
async addProcess(pid: number): Promise<void> {
|
|
673
|
+
fs.writeFileSync(`${this.cgroupPath}/cgroup.procs`, pid.toString());
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
async getStats(): Promise<{
|
|
677
|
+
memoryCurrent: number;
|
|
678
|
+
memoryPeak: number;
|
|
679
|
+
cpuUsage: number;
|
|
680
|
+
pidsCurrent: number;
|
|
681
|
+
}> {
|
|
682
|
+
return {
|
|
683
|
+
memoryCurrent: parseInt(fs.readFileSync(`${this.cgroupPath}/memory.current`, 'utf-8')),
|
|
684
|
+
memoryPeak: parseInt(fs.readFileSync(`${this.cgroupPath}/memory.peak`, 'utf-8') || '0'),
|
|
685
|
+
cpuUsage: parseInt(fs.readFileSync(`${this.cgroupPath}/cpu.stat`, 'utf-8').split('\n')[0].split(' ')[1]),
|
|
686
|
+
pidsCurrent: parseInt(fs.readFileSync(`${this.cgroupPath}/pids.current`, 'utf-8')),
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async destroy(): Promise<void> {
|
|
691
|
+
// Matar todos os processos
|
|
692
|
+
try {
|
|
693
|
+
execSync(`kill -9 $(cat ${this.cgroupPath}/cgroup.procs) 2>/dev/null`);
|
|
694
|
+
} catch (e) {}
|
|
695
|
+
// Remover cgroup
|
|
696
|
+
fs.rmdirSync(this.cgroupPath, { recursive: true } as any);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
FASE 5: ORM/DB COMO STORAGE
|
|
701
|
+
5.1 Drizzle ORM + Turso/libSQL
|
|
702
|
+
// packages/core/src/db/schema.ts
|
|
703
|
+
|
|
704
|
+
import { sqliteTable, text, integer, blob } from 'drizzle-orm/sqlite-core';
|
|
705
|
+
|
|
706
|
+
export const sandboxes = sqliteTable('sandboxes', {
|
|
707
|
+
id: text('id').primaryKey(),
|
|
708
|
+
userId: text('user_id').notNull(),
|
|
709
|
+
status: text('status').notNull(), // 'running' | 'stopped' | 'paused'
|
|
710
|
+
cpu: integer('cpu').default(2),
|
|
711
|
+
ramGb: integer('ram_gb').default(8),
|
|
712
|
+
diskGb: integer('disk_gb').default(10),
|
|
713
|
+
rootfs: blob('rootfs'), // Filesystem snapshot em SQLite
|
|
714
|
+
workspace: blob('workspace'), // Workspace files em SQLite
|
|
715
|
+
createdAt: text('created_at').default('CURRENT_TIMESTAMP'),
|
|
716
|
+
updatedAt: text('updated_at').default('CURRENT_TIMESTAMP'),
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
export const sandboxFiles = sqliteTable('sandbox_files', {
|
|
720
|
+
id: text('id').primaryKey(),
|
|
721
|
+
sandboxId: text('sandbox_id').references(() => sandboxes.id),
|
|
722
|
+
path: text('path').notNull(),
|
|
723
|
+
content: blob('content').notNull(), // Arquivo como BLOB no SQLite
|
|
724
|
+
size: integer('size'),
|
|
725
|
+
modifiedAt: text('modified_at').default('CURRENT_TIMESTAMP'),
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
export const sandboxSnapshots = sqliteTable('sandbox_snapshots', {
|
|
729
|
+
id: text('id').primaryKey(),
|
|
730
|
+
sandboxId: text('sandbox_id').references(() => sandboxes.id),
|
|
731
|
+
name: text('name').notNull(),
|
|
732
|
+
rootfs: blob('rootfs').notNull(), // Snapshot completo do filesystem
|
|
733
|
+
memory: blob('memory'), // Estado da memória
|
|
734
|
+
createdAt: text('created_at').default('CURRENT_TIMESTAMP'),
|
|
735
|
+
});
|
|
736
|
+
// packages/core/src/db/turso.ts
|
|
737
|
+
|
|
738
|
+
import { createClient } from '@libsql/client';
|
|
739
|
+
import { drizzle } from 'drizzle-orm/libsql';
|
|
740
|
+
import { sandboxes, sandboxFiles } from './schema';
|
|
741
|
+
|
|
742
|
+
// Conexão com Turso (SQLite distribuído, 5GB grátis)
|
|
743
|
+
const client = createClient({
|
|
744
|
+
url: process.env.TURSO_DATABASE_URL!, // libsql://your-db.turso.io
|
|
745
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
export const db = drizzle(client, { schema: { sandboxes, sandboxFiles } });
|
|
749
|
+
|
|
750
|
+
// Converter filesystem inteiro para SQLite (Storage → "RAM")
|
|
751
|
+
export async function filesystemToSQLite(sandboxId: string, files: Map<string, Buffer>) {
|
|
752
|
+
for (const [path, content] of files) {
|
|
753
|
+
const id = `${sandboxId}:${path}`;
|
|
754
|
+
await db.insert(sandboxFiles).values({
|
|
755
|
+
id,
|
|
756
|
+
sandboxId,
|
|
757
|
+
path,
|
|
758
|
+
content,
|
|
759
|
+
size: content.length,
|
|
760
|
+
}).onConflictDoUpdate({
|
|
761
|
+
target: sandboxFiles.id,
|
|
762
|
+
set: { content, size: content.length, modifiedAt: new Date().toISOString() },
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Converter SQLite para filesystem (SQLite → "RAM")
|
|
768
|
+
export async function sqliteToFilesystem(sandboxId: string): Promise<Map<string, Buffer>> {
|
|
769
|
+
const files = await db.select().from(sandboxFiles).where(
|
|
770
|
+
eq(sandboxFiles.sandboxId, sandboxId)
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
const result = new Map<string, Buffer>();
|
|
774
|
+
for (const file of files) {
|
|
775
|
+
result.set(file.path, file.content as Buffer);
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
FASE 6: CARREGAMENTO VIA CDN (npm packages instantâneos)
|
|
781
|
+
// packages/core/src/storage/npm-cdn.ts
|
|
782
|
+
|
|
783
|
+
interface CDNPackage {
|
|
784
|
+
name: string;
|
|
785
|
+
version: string;
|
|
786
|
+
files: Map<string, string>;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
export class NPMLoader {
|
|
790
|
+
private cache = new Map<string, CDNPackage>();
|
|
791
|
+
|
|
792
|
+
// Carregar pacote via esm.sh (transforma em ES module automaticamente)
|
|
793
|
+
async loadPackage(name: string, version: string): Promise<string> {
|
|
794
|
+
const key = `${name}@${version}`;
|
|
795
|
+
|
|
796
|
+
if (this.cache.has(key)) {
|
|
797
|
+
return this.cache.get(key)!.files.get('index.js')!;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Buscar via esm.sh
|
|
801
|
+
const url = `https://esm.sh/${name}@${version}`;
|
|
802
|
+
const response = await fetch(url);
|
|
803
|
+
const code = await response.text();
|
|
804
|
+
|
|
805
|
+
this.cache.set(key, {
|
|
806
|
+
name,
|
|
807
|
+
version,
|
|
808
|
+
files: new Map([['index.js', code]]),
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
return code;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Carregar múltiplos pacotes via import maps (browser)
|
|
815
|
+
async generateImportMap(packages: Array<{ name: string; version: string }>): Promise<object> {
|
|
816
|
+
const imports: Record<string, string> = {};
|
|
817
|
+
|
|
818
|
+
for (const pkg of packages) {
|
|
819
|
+
imports[pkg.name] = `https://esm.sh/${pkg.name}@${pkg.version}`;
|
|
820
|
+
imports[`${pkg.name}/`] = `https://esm.sh/${pkg.name}@${pkg.version}/`;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return { imports };
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Carregar pacote via jsDelivr (backup, mais rápido)
|
|
827
|
+
async loadViaJsDelivr(name: string, version: string, file: string = ''): Promise<string> {
|
|
828
|
+
const url = file
|
|
829
|
+
? `https://cdn.jsdelivr.net/npm/${name}@${version}/${file}`
|
|
830
|
+
: `https://cdn.jsdelivr.net/npm/${name}@${version}`;
|
|
831
|
+
|
|
832
|
+
const response = await fetch(url);
|
|
833
|
+
return await response.text();
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Carregar pacote do GitHub (para packages não publicados no npm)
|
|
837
|
+
async loadFromGitHub(owner: string, repo: string, path: string): Promise<string> {
|
|
838
|
+
const url = `https://raw.githubusercontent.com/${owner}/${repo}/main/${path}`;
|
|
839
|
+
const response = await fetch(url);
|
|
840
|
+
return await response.text();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
FASE 7: ORQUESTAÇÃO MULTI-PLATAFORMA
|
|
845
|
+
// packages/core/src/orchestrator/multi-platform.ts
|
|
846
|
+
|
|
847
|
+
interface ComputeProvider {
|
|
848
|
+
name: string;
|
|
849
|
+
cpu: number;
|
|
850
|
+
ramGb: number;
|
|
851
|
+
maxDuration: number; // ms
|
|
852
|
+
cost: 'free' | 'paid';
|
|
853
|
+
execute: (config: SandboxConfig) => Promise<SandboxResult>;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export class MultiPlatformOrchestrator {
|
|
857
|
+
private providers: ComputeProvider[] = [
|
|
858
|
+
// 1. GitHub Actions (ILIMITADO para repos públicos)
|
|
859
|
+
{
|
|
860
|
+
name: 'github-actions',
|
|
861
|
+
cpu: 4,
|
|
862
|
+
ramGb: 16,
|
|
863
|
+
maxDuration: 6 * 60 * 60 * 1000, // 6 horas
|
|
864
|
+
cost: 'free',
|
|
865
|
+
execute: async (config) => {
|
|
866
|
+
// Trigger GitHub Actions workflow
|
|
867
|
+
const response = await fetch(`https://api.github.com/repos/${config.repo}/actions/workflows/${config.workflow}/dispatches`, {
|
|
868
|
+
method: 'POST',
|
|
869
|
+
headers: {
|
|
870
|
+
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
|
871
|
+
Accept: 'application/vnd.github.v3+json',
|
|
872
|
+
},
|
|
873
|
+
body: JSON.stringify({
|
|
874
|
+
ref: 'main',
|
|
875
|
+
inputs: { sandbox_id: config.id, code: config.code },
|
|
876
|
+
}),
|
|
877
|
+
});
|
|
878
|
+
// Poll para resultado
|
|
879
|
+
return this.pollGitHubResult(config.repo, config.id);
|
|
880
|
+
},
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
// 2. GitLab CI (400 min/mês grátis)
|
|
884
|
+
{
|
|
885
|
+
name: 'gitlab-ci',
|
|
886
|
+
cpu: 2,
|
|
887
|
+
ramGb: 8,
|
|
888
|
+
maxDuration: 3 * 60 * 60 * 1000, // 3 horas
|
|
889
|
+
cost: 'free',
|
|
890
|
+
execute: async (config) => {
|
|
891
|
+
// Trigger GitLab pipeline via API
|
|
892
|
+
const response = await fetch(`https://gitlab.com/api/v4/projects/${config.projectId}/pipeline`, {
|
|
893
|
+
method: 'POST',
|
|
894
|
+
headers: {
|
|
895
|
+
Authorization: `Bearer ${process.env.GITLAB_TOKEN}`,
|
|
896
|
+
'Content-Type': 'application/json',
|
|
897
|
+
},
|
|
898
|
+
body: JSON.stringify({
|
|
899
|
+
ref: 'main',
|
|
900
|
+
variables: [
|
|
901
|
+
{ key: 'SANDBOX_ID', value: config.id },
|
|
902
|
+
{ key: 'SANDBOX_CODE', value: config.code },
|
|
903
|
+
],
|
|
904
|
+
}),
|
|
905
|
+
});
|
|
906
|
+
return this.pollGitLabResult(config.projectId, config.id);
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
|
|
910
|
+
// 3. Oracle Cloud Always Free (permanente, 2 OCPU ARM, 12GB RAM)
|
|
911
|
+
{
|
|
912
|
+
name: 'oracle-cloud',
|
|
913
|
+
cpu: 2,
|
|
914
|
+
ramGb: 12,
|
|
915
|
+
maxDuration: Infinity, // Sem limite
|
|
916
|
+
cost: 'free',
|
|
917
|
+
execute: async (config) => {
|
|
918
|
+
// SSH no Oracle Cloud VM
|
|
919
|
+
const ssh = `ssh -i ~/.ssh/oracle_key opc@${process.env.ORACLE_IP}`;
|
|
920
|
+
// Executar sandbox via gVisor
|
|
921
|
+
return execSync(`${ssh} "docker run --runtime=runsc ... ${config.code}"`);
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
|
|
925
|
+
// 4. HuggingFace Spaces (2 CPU, 16GB RAM grátis)
|
|
926
|
+
{
|
|
927
|
+
name: 'huggingface',
|
|
928
|
+
cpu: 2,
|
|
929
|
+
ramGb: 16,
|
|
930
|
+
maxDuration: 24 * 60 * 60 * 1000, // 24 horas (sleep após idle)
|
|
931
|
+
cost: 'free',
|
|
932
|
+
execute: async (config) => {
|
|
933
|
+
// Deploy como Gradio app
|
|
934
|
+
const appCode = `
|
|
935
|
+
import gradio as gr
|
|
936
|
+
import subprocess, json
|
|
937
|
+
|
|
938
|
+
def run_sandbox(code):
|
|
939
|
+
result = subprocess.run(['python3', '-c', code], capture_output=True, text=True, timeout=300)
|
|
940
|
+
return result.stdout + result.stderr
|
|
941
|
+
|
|
942
|
+
demo = gr.Interface(fn=run_sandbox, inputs="code", outputs="text")
|
|
943
|
+
demo.launch()
|
|
944
|
+
`;
|
|
945
|
+
// Push para HuggingFace Space
|
|
946
|
+
const api = new HfApi();
|
|
947
|
+
await api.uploadFolder({
|
|
948
|
+
folderPath: './space',
|
|
949
|
+
repoId: `user/sandbox-${config.id}`,
|
|
950
|
+
repoType: 'space',
|
|
951
|
+
});
|
|
952
|
+
},
|
|
953
|
+
},
|
|
954
|
+
|
|
955
|
+
// 5. Kaggle (4 CPU, 29GB RAM, GPU grátis)
|
|
956
|
+
{
|
|
957
|
+
name: 'kaggle',
|
|
958
|
+
cpu: 4,
|
|
959
|
+
ramGb: 29,
|
|
960
|
+
maxDuration: 12 * 60 * 60 * 1000, // 12 horas
|
|
961
|
+
cost: 'free',
|
|
962
|
+
execute: async (config) => {
|
|
963
|
+
// Criar Kaggle notebook
|
|
964
|
+
const kernelMeta = {
|
|
965
|
+
id: `sandbox-${config.id}`,
|
|
966
|
+
title: `Sandbox ${config.id}`,
|
|
967
|
+
code_file: 'kernel.py',
|
|
968
|
+
language: 'python',
|
|
969
|
+
kernel_type: 'notebook',
|
|
970
|
+
is_private: false,
|
|
971
|
+
};
|
|
972
|
+
// Push e executar
|
|
973
|
+
execSync(`kaggle kernels push -p ./kernel_dir`);
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
];
|
|
977
|
+
|
|
978
|
+
// Auto-selecionar melhor provider baseado nos requisitos
|
|
979
|
+
selectProvider(requirements: { cpu: number; ramGb: number; duration: number }): ComputeProvider {
|
|
980
|
+
const suitable = this.providers.filter(p =>
|
|
981
|
+
p.cpu >= requirements.cpu &&
|
|
982
|
+
p.ramGb >= requirements.ramGb &&
|
|
983
|
+
p.maxDuration >= requirements.duration
|
|
984
|
+
);
|
|
985
|
+
|
|
986
|
+
// Priorizar: Oracle Cloud (permanente) > GitHub (ilimitado) > GitLab > HuggingFace > Kaggle
|
|
987
|
+
const priority = ['oracle-cloud', 'github-actions', 'huggingface', 'gitlab-ci', 'kaggle'];
|
|
988
|
+
|
|
989
|
+
for (const name of priority) {
|
|
990
|
+
const provider = suitable.find(p => p.name === name);
|
|
991
|
+
if (provider) return provider;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
throw new Error('Nenhum provider adequado encontrado');
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
FASE 8: GERENCIAMENTO DE ESTADO (Sandbox Pausado/Retomado)
|
|
999
|
+
// packages/core/src/sandbox/manager.ts
|
|
1000
|
+
|
|
1001
|
+
import { HybridMemoryManager } from '../memory/hybrid';
|
|
1002
|
+
import { GVisorIsolate } from './isolate';
|
|
1003
|
+
import { CGroupsManager } from './resources';
|
|
1004
|
+
import { db, filesystemToSQLite, sqliteToFilesystem } from '../db/turso';
|
|
1005
|
+
|
|
1006
|
+
interface SandboxState {
|
|
1007
|
+
id: string;
|
|
1008
|
+
status: 'running' | 'paused' | 'stopped';
|
|
1009
|
+
cpu: number;
|
|
1010
|
+
ramGb: number;
|
|
1011
|
+
rootfsSnapshot: Buffer; // Snapshot do filesystem em memória
|
|
1012
|
+
memorySnapshot: Buffer; // Estado da RAM em memória
|
|
1013
|
+
workspaceFiles: Map<string, Buffer>;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
export class SandboxManager {
|
|
1017
|
+
private memory = new HybridMemoryManager();
|
|
1018
|
+
private isolate = new GVisorIsolate();
|
|
1019
|
+
private activeCGroups = new Map<string, CGroupsManager>();
|
|
1020
|
+
|
|
1021
|
+
async create(config: SandboxConfig): Promise<string> {
|
|
1022
|
+
const id = `sandbox-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1023
|
+
|
|
1024
|
+
// 1. Inicializar memória híbrida
|
|
1025
|
+
await this.memory.init();
|
|
1026
|
+
|
|
1027
|
+
// 2. Criar filesystem base (carregar de CDN se necessário)
|
|
1028
|
+
const baseFiles = new Map<string, Buffer>();
|
|
1029
|
+
|
|
1030
|
+
// Carregar Node.js runtime do CDN se necessário
|
|
1031
|
+
const nodeRuntime = await this.memory.loadFromCDN('node', '20.0.0');
|
|
1032
|
+
baseFiles.set('/usr/bin/node', Buffer.from(nodeRuntime));
|
|
1033
|
+
|
|
1034
|
+
// 3. Salvar filesystem no SQLite (persistência)
|
|
1035
|
+
await filesystemToSQLite(id, baseFiles);
|
|
1036
|
+
|
|
1037
|
+
// 4. Criar sandbox com gVisor
|
|
1038
|
+
const containerId = await this.isolate.create({
|
|
1039
|
+
...config,
|
|
1040
|
+
id,
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
// 5. Configurar limites de recursos
|
|
1044
|
+
const cgroups = new CGroupsManager(id);
|
|
1045
|
+
await cgroups.setup({
|
|
1046
|
+
cpuQuota: `${config.cpu * 100000} 100000`,
|
|
1047
|
+
memoryMax: `${config.ramGb * 1024 * 1024 * 1024}`,
|
|
1048
|
+
memoryHigh: `${(config.ramGb - 2) * 1024 * 1024 * 1024}`, // 2GB below max
|
|
1049
|
+
pidsMax: 512,
|
|
1050
|
+
ioMax: '8:0 rbps=104857600 wbps=104857600',
|
|
1051
|
+
});
|
|
1052
|
+
this.activeCGroups.set(id, cgroups);
|
|
1053
|
+
|
|
1054
|
+
// 6. Criar entry no banco
|
|
1055
|
+
await db.insert(sandboxes).values({
|
|
1056
|
+
id,
|
|
1057
|
+
userId: config.userId || 'anonymous',
|
|
1058
|
+
status: 'running',
|
|
1059
|
+
cpu: config.cpu,
|
|
1060
|
+
ramGb: config.ramGb,
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
return id;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
async pause(sandboxId: string): Promise<void> {
|
|
1067
|
+
// 1. Pausar processo no gVisor
|
|
1068
|
+
await this.isolate.pause(sandboxId);
|
|
1069
|
+
|
|
1070
|
+
// 2. Capturar snapshot da memória
|
|
1071
|
+
const cgroups = this.activeCGroups.get(sandboxId);
|
|
1072
|
+
const memStats = await cgroups!.getStats();
|
|
1073
|
+
const memSnapshot = Buffer.alloc(memStats.memoryPeak);
|
|
1074
|
+
// ... copiar memória do cgroup para buffer
|
|
1075
|
+
|
|
1076
|
+
// 3. Capturar snapshot do filesystem
|
|
1077
|
+
const files = await sqliteToFilesystem(sandboxId);
|
|
1078
|
+
const rootfsSnapshot = Buffer.from(JSON.stringify(Array.from(files.entries())));
|
|
1079
|
+
|
|
1080
|
+
// 4. Salvar snapshots no SQLite
|
|
1081
|
+
await db.insert(sandboxSnapshots).values({
|
|
1082
|
+
id: `snapshot-${sandboxId}`,
|
|
1083
|
+
sandboxId,
|
|
1084
|
+
name: 'pause-snapshot',
|
|
1085
|
+
rootfs: rootfsSnapshot,
|
|
1086
|
+
memory: memSnapshot,
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
// 5. Atualizar status
|
|
1090
|
+
await db.update(sandboxes)
|
|
1091
|
+
.set({ status: 'paused' })
|
|
1092
|
+
.where(eq(sandboxes.id, sandboxId));
|
|
1093
|
+
|
|
1094
|
+
// 6. Liberar recursos
|
|
1095
|
+
await cgroups!.destroy();
|
|
1096
|
+
this.activeCGroups.delete(sandboxId);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
async resume(sandboxId: string): Promise<void> {
|
|
1100
|
+
// 1. Carregar snapshot do SQLite
|
|
1101
|
+
const [snapshot] = await db.select()
|
|
1102
|
+
.from(sandboxSnapshots)
|
|
1103
|
+
.where(eq(sandboxSnapshots.sandboxId, sandboxId))
|
|
1104
|
+
.orderBy(desc(sandboxSnapshots.createdAt))
|
|
1105
|
+
.limit(1);
|
|
1106
|
+
|
|
1107
|
+
// 2. Restaurar filesystem
|
|
1108
|
+
const files = new Map<string, Buffer>(JSON.parse(snapshot.rootfs.toString()));
|
|
1109
|
+
await filesystemToSQLite(sandboxId, files);
|
|
1110
|
+
|
|
1111
|
+
// 3. Criar novo sandbox
|
|
1112
|
+
const [sandbox] = await db.select().from(sandboxes).where(eq(sandboxes.id, sandboxId));
|
|
1113
|
+
await this.create({
|
|
1114
|
+
id: sandboxId,
|
|
1115
|
+
cpu: sandbox.cpu!,
|
|
1116
|
+
ramGb: sandbox.ramGb!,
|
|
1117
|
+
code: '', // Código restaurado do workspace
|
|
1118
|
+
language: 'javascript',
|
|
1119
|
+
timeout: 3600000,
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
// 4. Restaurar memória via mmap
|
|
1123
|
+
const mmapBridge = await this.memory.bridgeToMemory(
|
|
1124
|
+
`/tmp/sandbox-memory/${sandboxId}`
|
|
1125
|
+
);
|
|
1126
|
+
// ... restaurar estado da memória do snapshot
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
async destroy(sandboxId: string): Promise<void> {
|
|
1130
|
+
// 1. Destruir gVisor sandbox
|
|
1131
|
+
await this.isolate.destroy(`sandbox-${sandboxId}`);
|
|
1132
|
+
|
|
1133
|
+
// 2. Destruir cgroups
|
|
1134
|
+
const cgroups = this.activeCGroups.get(sandboxId);
|
|
1135
|
+
if (cgroups) {
|
|
1136
|
+
await cgroups.destroy();
|
|
1137
|
+
this.activeCGroups.delete(sandboxId);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// 3. Manter snapshots no SQLite (para future resume)
|
|
1141
|
+
await db.update(sandboxes)
|
|
1142
|
+
.set({ status: 'stopped' })
|
|
1143
|
+
.where(eq(sandboxes.id, sandboxId));
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
FASE 9: PACOTE NPM + DEPLOY
|
|
1148
|
+
9.1 Package.json
|
|
1149
|
+
{
|
|
1150
|
+
"name": "@zai/sandbox",
|
|
1151
|
+
"version": "1.0.0",
|
|
1152
|
+
"description": "Open-source sandbox platform using free infrastructure",
|
|
1153
|
+
"main": "dist/index.js",
|
|
1154
|
+
"types": "dist/index.d.ts",
|
|
1155
|
+
"scripts": {
|
|
1156
|
+
"build": "turbo build",
|
|
1157
|
+
"dev": "turbo dev",
|
|
1158
|
+
"test": "vitest",
|
|
1159
|
+
"lint": "eslint",
|
|
1160
|
+
"typecheck": "tsc --noEmit"
|
|
1161
|
+
},
|
|
1162
|
+
"dependencies": {
|
|
1163
|
+
"memfs": "^4.64.0",
|
|
1164
|
+
"@platformatic/vfs": "^1.0.0",
|
|
1165
|
+
"drizzle-orm": "^0.38.0",
|
|
1166
|
+
"@libsql/client": "^0.6.0",
|
|
1167
|
+
"better-sqlite3": "^11.0.0",
|
|
1168
|
+
"tinypool": "^1.0.0",
|
|
1169
|
+
"isolated-vm": "^5.0.0",
|
|
1170
|
+
"mmap-io": "^1.0.0",
|
|
1171
|
+
"@huggingface/hub": "^1.0.0"
|
|
1172
|
+
},
|
|
1173
|
+
"devDependencies": {
|
|
1174
|
+
"drizzle-kit": "^0.28.0",
|
|
1175
|
+
"typescript": "^5.7.0",
|
|
1176
|
+
"vitest": "^3.0.0",
|
|
1177
|
+
"turbo": "^2.4.0"
|
|
1178
|
+
},
|
|
1179
|
+
"publishConfig": {
|
|
1180
|
+
"access": "public",
|
|
1181
|
+
"registry": "https://registry.npmjs.org"
|
|
1182
|
+
},
|
|
1183
|
+
"files": ["dist", "README.md"]
|
|
1184
|
+
}
|
|
1185
|
+
9.2 GitHub Actions Workflow Principal
|
|
1186
|
+
# .github/workflows/build-deploy.yml
|
|
1187
|
+
name: Build & Deploy ZAI Sandbox
|
|
1188
|
+
|
|
1189
|
+
on:
|
|
1190
|
+
push:
|
|
1191
|
+
branches: [main]
|
|
1192
|
+
pull_request:
|
|
1193
|
+
branches: [main]
|
|
1194
|
+
|
|
1195
|
+
jobs:
|
|
1196
|
+
build:
|
|
1197
|
+
runs-on: ubuntu-latest
|
|
1198
|
+
steps:
|
|
1199
|
+
- uses: actions/checkout@v4
|
|
1200
|
+
- uses: actions/setup-node@v4
|
|
1201
|
+
with:
|
|
1202
|
+
node-version: 22
|
|
1203
|
+
|
|
1204
|
+
- name: Install dependencies
|
|
1205
|
+
run: npm ci
|
|
1206
|
+
|
|
1207
|
+
- name: Build
|
|
1208
|
+
run: npm run build
|
|
1209
|
+
|
|
1210
|
+
- name: Test
|
|
1211
|
+
run: npm test
|
|
1212
|
+
|
|
1213
|
+
- name: Publish to npm
|
|
1214
|
+
if: github.ref == 'refs/heads/main'
|
|
1215
|
+
run: npm publish
|
|
1216
|
+
env:
|
|
1217
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
1218
|
+
|
|
1219
|
+
- name: Deploy to Vercel
|
|
1220
|
+
if: github.ref == 'refs/heads/main'
|
|
1221
|
+
uses: amondnet/vercel-action@v25
|
|
1222
|
+
with:
|
|
1223
|
+
vercel-token: ${{ secrets.VERCEL_TOKEN }}
|
|
1224
|
+
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
|
|
1225
|
+
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
|
|
1226
|
+
vercel-args: '--prod'
|
|
1227
|
+
|
|
1228
|
+
FASE 10: RESUMO DE RECURSOS GRATUITOS
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
Plataforma
|
|
1233
|
+
CPU
|
|
1234
|
+
RAM
|
|
1235
|
+
Duração
|
|
1236
|
+
Armazenamento
|
|
1237
|
+
Melhor Para
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
GitHub Actions (repo público)
|
|
1242
|
+
4 vCPU
|
|
1243
|
+
16 GB
|
|
1244
|
+
6h/job
|
|
1245
|
+
14 GB SSD
|
|
1246
|
+
Compute principal (ILIMITADO)
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
Oracle Cloud Always Free
|
|
1250
|
+
2 OCPU ARM
|
|
1251
|
+
12 GB
|
|
1252
|
+
Permanente
|
|
1253
|
+
200 GB
|
|
1254
|
+
Servidor permanente
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
HuggingFace Spaces
|
|
1258
|
+
2 vCPU
|
|
1259
|
+
16 GB
|
|
1260
|
+
Indefinido
|
|
1261
|
+
50 GB
|
|
1262
|
+
ML/AI sandboxes
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
Kaggle
|
|
1266
|
+
4 cores
|
|
1267
|
+
29 GB
|
|
1268
|
+
12h/session
|
|
1269
|
+
20 GB
|
|
1270
|
+
GPU (T4 32GB VRAM)
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
GitLab CI
|
|
1274
|
+
2 vCPU
|
|
1275
|
+
8 GB
|
|
1276
|
+
3h/job
|
|
1277
|
+
30 GB
|
|
1278
|
+
Compute secundário
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
Cloudflare Workers
|
|
1282
|
+
Serverless
|
|
1283
|
+
128 MB
|
|
1284
|
+
Indefinido
|
|
1285
|
+
10 GB R2
|
|
1286
|
+
API/Orquestração
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
Vercel
|
|
1290
|
+
1 vCPU
|
|
1291
|
+
2 GB
|
|
1292
|
+
300s
|
|
1293
|
+
1 GB
|
|
1294
|
+
Frontend/API
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
Turso/libSQL
|
|
1298
|
+
-
|
|
1299
|
+
-
|
|
1300
|
+
-
|
|
1301
|
+
5 GB grátis
|
|
1302
|
+
Storage SQLite
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
jsDelivr/esm.sh
|
|
1306
|
+
-
|
|
1307
|
+
-
|
|
1308
|
+
-
|
|
1309
|
+
-
|
|
1310
|
+
CDN instantâneo
|
|
1311
|
+
|
|
1312
|
+
|
|
1313
|
+
Memória Total Disponível (Grátis)
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
Tier
|
|
1318
|
+
Fonte
|
|
1319
|
+
RAM/Storage
|
|
1320
|
+
Como Usar
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
RAM real
|
|
1325
|
+
GitHub Actions
|
|
1326
|
+
16 GB
|
|
1327
|
+
gVisor sandbox
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
RAM comprimida
|
|
1331
|
+
zram
|
|
1332
|
+
~32 GB (2x)
|
|
1333
|
+
/dev/zram0
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
tmpfs
|
|
1337
|
+
Linux
|
|
1338
|
+
4 GB
|
|
1339
|
+
/tmp sandbox
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
mmap
|
|
1343
|
+
Storage→RAM
|
|
1344
|
+
Ilimitado
|
|
1345
|
+
Arquivos mapeados
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
SQLite VFS
|
|
1349
|
+
Turso
|
|
1350
|
+
5 GB
|
|
1351
|
+
Arquivos como BLOBs
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
Object Storage
|
|
1355
|
+
Cloudflare R2
|
|
1356
|
+
10 GB
|
|
1357
|
+
Snapshots
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
ARM VM
|
|
1361
|
+
Oracle Cloud
|
|
1362
|
+
12 GB RAM
|
|
1363
|
+
Sempre ligado
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
GPU
|
|
1367
|
+
Kaggle
|
|
1368
|
+
32 GB VRAM
|
|
1369
|
+
30h/semana
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
Total potencial: ~16 GB RAM real + 32 GB zram + 12 GB Oracle + 32 GB GPU VRAM = ~92 GB de "memória"
|
|
1373
|
+
|
|
1374
|
+
COMO RODAR (Passo a Passo)
|
|
1375
|
+
# 1. Clonar o repositório
|
|
1376
|
+
git clone https://github.com/seu-usuario/zai-sandbox.git
|
|
1377
|
+
cd zai-sandbox
|
|
1378
|
+
|
|
1379
|
+
# 2. Instalar dependências
|
|
1380
|
+
npm install
|
|
1381
|
+
|
|
1382
|
+
# 3. Configurar variáveis de ambiente
|
|
1383
|
+
cp .env.example .env
|
|
1384
|
+
# Editar .env com:
|
|
1385
|
+
# - GITHUB_TOKEN
|
|
1386
|
+
# - GITLAB_TOKEN
|
|
1387
|
+
# - TURSO_DATABASE_URL
|
|
1388
|
+
# - TURSO_AUTH_TOKEN
|
|
1389
|
+
# - CLOUDFLARE_ACCOUNT_ID
|
|
1390
|
+
# - CLOUDFLARE_R2_TOKEN
|
|
1391
|
+
# - HUGGINGFACE_TOKEN
|
|
1392
|
+
|
|
1393
|
+
# 4. Criar schema no Turso
|
|
1394
|
+
npx drizzle-kit push
|
|
1395
|
+
|
|
1396
|
+
# 5. Build do projeto
|
|
1397
|
+
npm run build
|
|
1398
|
+
|
|
1399
|
+
# 6. Criar um sandbox
|
|
1400
|
+
node -e "
|
|
1401
|
+
const { SandboxManager } = require('./dist');
|
|
1402
|
+
const manager = new SandboxManager();
|
|
1403
|
+
const id = await manager.create({
|
|
1404
|
+
cpu: 4,
|
|
1405
|
+
ramGb: 16,
|
|
1406
|
+
code: 'console.log(\"Hello from ZAI Sandbox!\")',
|
|
1407
|
+
language: 'javascript',
|
|
1408
|
+
timeout: 60000,
|
|
1409
|
+
});
|
|
1410
|
+
console.log('Sandbox criado:', id);
|
|
1411
|
+
"
|
|
1412
|
+
|
|
1413
|
+
# 7. Deploy no Oracle Cloud (sempre disponível)
|
|
1414
|
+
ssh opc@seu-oracle-ip 'bash -s' < infra/scripts/setup-gvisor.sh
|
|
1415
|
+
|
|
1416
|
+
# 8. Configurar GitHub Actions
|
|
1417
|
+
# Copiar .github/workflows/sandbox-compute.yml
|
|
1418
|
+
|
|
1419
|
+
# 9. Publicar como npm package
|
|
1420
|
+
npm publish --access public
|
|
1421
|
+
|
|
1422
|
+
Este plano combina todas as suas ideias: memória de armazenamento convertida para RAM (via mmap, tmpfs, zram, SQLite VFS), compute gratuito (GitHub Actions, Oracle Cloud, HuggingFace, Kaggle), isolamento (gVisor, cgroups, seccomp), armazenamento (Cloudflare R2, Turso, GitHub Artifacts), e orquestração multi-plataforma. Tudo isso como um projeto open source publicado no npm.
|
|
1423
|
+
|
|
1424
|
+
Pesquisa completa! Aqui está o resumo:
|
|
1425
|
+
40 Pesquisas Realizadas - Resumo
|
|
1426
|
+
Infraestrutura Gratuita Disponível
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
Recurso
|
|
1431
|
+
Fonte
|
|
1432
|
+
Quanto
|
|
1433
|
+
|
|
1434
|
+
|
|
1435
|
+
|
|
1436
|
+
4 CPU + 16GB RAM
|
|
1437
|
+
GitHub Actions (repo público)
|
|
1438
|
+
Ilimitado
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
2 OCPU + 12GB RAM
|
|
1442
|
+
Oracle Cloud Always Free
|
|
1443
|
+
Permanente
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
2 CPU + 16GB RAM
|
|
1447
|
+
HuggingFace Spaces
|
|
1448
|
+
Indefinido
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
4 cores + 29GB RAM + GPU 32GB
|
|
1452
|
+
Kaggle
|
|
1453
|
+
30h/semana
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
2 CPU + 8GB RAM
|
|
1457
|
+
GitLab CI
|
|
1458
|
+
400 min/mês
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
5GB SQLite
|
|
1462
|
+
Turso/libSQL
|
|
1463
|
+
Grátis
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
10GB Object Storage
|
|
1467
|
+
Cloudflare R2
|
|
1468
|
+
Grátis
|
|
1469
|
+
|
|
1470
|
+
|
|
1471
|
+
CDN ilimitado
|
|
1472
|
+
jsDelivr/esm.sh
|
|
1473
|
+
Grátis
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
Memória Total Potencial: ~92 GB
|
|
1477
|
+
|
|
1478
|
+
16 GB RAM real (GitHub Actions)
|
|
1479
|
+
~32 GB zram (RAM comprimida 2x)
|
|
1480
|
+
12 GB Oracle Cloud (permanente)
|
|
1481
|
+
32 GB GPU VRAM (Kaggle)
|
|
1482
|
+
|
|
1483
|
+
Isolamento
|
|
1484
|
+
|
|
1485
|
+
gVisor - kernel em user-space (sem KVM)
|
|
1486
|
+
cgroups v2 - limitação de CPU/RAM
|
|
1487
|
+
seccomp/AppArmor - filtros de segurança
|
|
1488
|
+
OverlayFS - filesystem em camadas
|
|
1489
|
+
Podman rootless - sem daemon
|
|
1490
|
+
|
|
1491
|
+
Storage→RAM
|
|
1492
|
+
|
|
1493
|
+
mmap - converte arquivos em memória mapeada
|
|
1494
|
+
tmpfs - disco como RAM
|
|
1495
|
+
zram - RAM comprimida
|
|
1496
|
+
SQLite VFS - arquivos como BLOBs em memória
|
|
1497
|
+
memfs - filesystem virtual em RAM
|
|
1498
|
+
|
|
1499
|
+
O plano completo está acima com todos os códigos, configs, e passo a passo. Quer que eu implemente alguma parte específica?
|