Compare commits

...

29 Commits

Author SHA1 Message Date
eball
2622d15a48 fix(daemon): increase retry count for USB device detection 2026-03-04 17:23:00 +08:00
eball
8ce8b6c976 feat(cli): add time synchronization check using chronyc (#2616)
* feat(cli): add time synchronization check using chronyc

* feat(cli): update time synchronization check to validate stratum value
2026-03-04 14:43:42 +08:00
dkeven
cbab40a597 feat(bfl): use unified remote api env to query external ip (#2614)
* feat(bfl): use unified remote api env to query external ip (#2611)

* chore(bfl): update bfl image version to v0.4.41
2026-03-04 00:19:03 +08:00
dkeven
14691ea3ec feat(daemon): use unified remote api env to query external ip (#2613) 2026-03-04 00:18:25 +08:00
Power-One-2025
98f123fbf1 docs: add persona setup for OpenClaw tutorial (#2605)
* Add: Personalize OpenClaw

* simplify a message

* refinements for accuracy

* remove redundant text

* tag the step with Optional

* address comments

* adjust image size

* adjust image size
2026-03-03 21:43:07 +08:00
Jeremiah Lee
8f5023ce17 docs: fix broken shields images and architecture link in README (#2608)
- shields.io is case sensitive for the repo name (capital O Olares), resulting in "invalid" text rendering in badge
- architecture URL in docs moved without redirect
2026-03-03 20:39:48 +08:00
Yajing
4467bc61df docs: restructure factory reset and reinstall docs for Olares One (#2607)
* restructure factory reset and reinstall docs

* address comment
2026-03-03 20:35:52 +08:00
eball
85f1224616 cli: update etcd service template to use network-online.target (#2603) 2026-03-03 15:00:56 +08:00
eball
4b3a42d728 authelia: fix bug of sub-policy failed if set it to two-factor (#2601)
authelia: fix sub-policy failed when the main policy is internal
2026-03-03 13:11:00 +08:00
berg
3c821cbedb system frontend: fix system app launch and display bugs. (#2600)
* feat: update system frontend version

* feat: update system frontend version

---------

Co-authored-by: eball <liuy102@hotmail.com>
2026-03-03 13:10:36 +08:00
aby913
3129b295ce l4-bfl-proxy: fix multi users app custom domain (#2599)
* l4-bfl-proxy: fix multi users app custom domain (#2597)

* l4-bfl-proxy: fix multi users app custom domain

* fix: update error handling to check for both 403 and 404 HTTP status codes in upload scripts

---------

Co-authored-by: eball <liuy102@hotmail.com>
2026-03-03 13:09:56 +08:00
eball
76bde01b86 daemon: enhance USB device mounting by dynamically setting options based on filesystem type (#2596)
fix: enhance USB device mounting by dynamically setting options based on filesystem type
2026-03-03 13:09:11 +08:00
dkeven
32c652205d fix(appservice): avoid race condition between upgrade & applyenv (#2594)
* fix(appservice): avoid race condition between upgrade & applyenv (#2593)

* chore(appservice): update image version to 0.5.5

---------

Co-authored-by: eball <liuy102@hotmail.com>
2026-03-03 13:08:33 +08:00
Meow33
817316c1d6 docs: updated wise and desktop docs (#2586)
* docs: updated wise and desktop docs

* Refined expressions.

* Updated larepass index

* refine wording

* Updated translation.

* Update docs/manual/larepass/index.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

---------

Co-authored-by: yajing wang <413741312@qq.com>
Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-03-03 11:54:15 +08:00
eball
e03eb40ed8 fix: coscmd invalid parameters 2026-03-03 00:44:47 +08:00
eball
79b7d82748 Add VERSION environment variable to workflow 2026-03-02 23:59:23 +08:00
eball
20344416f8 fix: update error handling to check for both 403 and 404 HTTP status codes in upload scripts 2026-03-02 22:33:27 +08:00
eball
8c59050529 fix: remove unnecessary 'cp' argument from coscmd upload commands in release workflows 2026-03-02 15:18:40 +08:00
eball
f932d10916 fix: update upload command in release workflows to remove unnecessary 'cp' argument 2026-03-02 15:15:45 +08:00
eball
e4762d880b fix: remove public-read ACL from coscmd upload commands in release workflows 2026-03-02 15:08:27 +08:00
eball
017b2e2acd ci: change cdn backend storage to cos (#2592) 2026-03-02 14:55:26 +08:00
dkeven
d3adeced3f fix(cli): dynamic creation of nvidia runtimeclass (#2591) 2026-03-02 13:50:29 +08:00
hysyeah
48038504a7 fix: add kubeblocks addon chart image to manifest (#2590) 2026-03-02 13:50:04 +08:00
Yajing
d17d8fca14 docs: update Windows local access steps & tidy wording (#2587)
update Windows .local access & tidy wording
2026-03-02 13:23:57 +08:00
Teng
35770fbe46 docs: fix model name used in tutorial (#2582)
* fix model name used in tutorial

* Update docs/use-cases/openclaw.md

---------

Co-authored-by: Power-One-2025 <zhengchunhong@bytetrade.io>
2026-02-28 22:37:40 +08:00
Yajing
39cb3335d6 docs: fix & streamline ssh access (#2584)
fix & streamline ssh access
2026-02-28 22:29:49 +08:00
Yajing
0e1208d555 docs: add how to check SSH password in vault (#2571)
* add how to check SSH password in vault

* reuse reset ssh content, improve wording & flow
2026-02-28 14:24:10 +08:00
Yajing
65fa0c0da8 docs: add factory reset via BIOS and reinstall via USB (#2576)
* add factory reset via BIOS and reinstall via USB

* refine wording & add screenshots

* add zh docs

* address comments
2026-02-28 13:12:21 +08:00
eball
cf7125aac8 cli, daemon: enhance DGX Spark support and update GPU type handling (#2496)
* feat(gpu): enhance DGX Spark support and update GPU type handling

* feat(amdgpu): refactor AMD GPU detection and support for GB10 chip and APU

* feat(connector): enhance GB10 chip detection with environment variable support

* feat(gpu): enhance DGX Spark support and update GPU type handling

* feat(amdgpu): refactor AMD GPU detection and support for GB10 chip and APU

* feat(connector): enhance GB10 chip detection with environment variable support

* feat: add nvidia device plugin for gb10

* fix(gpu): update pod selector for hami-device-plugin based on GB10 chip detection

fix(deploy): bump app-service image version to 0.4.78

* feat: enable CGO for building on ARM architecture and adjust build constraints for Linux

* feat: enhance multi-architecture support for ARM64 in release workflow

* feat: update multi-arch setup for ARM64 in release workflow

* feat: enhance ARM64 multi-architecture support in release workflow

* feat: streamline ARM64 cross-compilation setup in release workflow

* feat: enhance ARM64 support by adding architecture-specific package installations

* feat: update ARM64 package sources in release workflow for improved compatibility

* feat: amd device plugin and container toolkit install

* refactor: remove GB10 chip type check from GPU info update

* feat(gpu): update hami version to v2.6.10-compatible for spark

* fix: remove gb10 device plugin checking

* fix: update klauspost/cpuid to v2.3.0

* fix: amd gpu check (#2522)

* feat: enhance storage device detection with USB serial properties

* feat: update hami version to v2.6.11-compatible-arm

* feat: add chip type support for AMD and NVIDIA GPUs in node label updates

* feat(gpu): supports auto binding GPU to app

* feat(gpu): remove chip type handling from GPU label updates

* feat(gpu): remove GPU type specification from DaemonSet and values.yaml

* feat(gpu): remove GB10 device plugin installation and related checks

* feat(gpu): update HAMi to v2.6.11

---------

Co-authored-by: dkeven <dkvvven@gmail.com>
Co-authored-by: hys <hysyeah@gmail.com>
2026-02-28 11:44:02 +08:00
130 changed files with 2769 additions and 1465 deletions

View File

@@ -11,10 +11,22 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
bash build/build-redis.sh linux/amd64 glibc-231

View File

@@ -11,12 +11,24 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
bash build/build-redis.sh linux/amd64
push-arm64:
@@ -34,10 +46,22 @@ jobs:
run: |
sudo apt install -y make gcc
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
sudo -E sh -c "bash build/build-redis.sh linux/arm64 && rm -rf redis*"

View File

@@ -11,10 +11,22 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
bash build/build-ubuntu2204.sh

View File

@@ -11,10 +11,23 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
bash build/build-wsl-install-msi.sh

View File

@@ -60,14 +60,6 @@ jobs:
- name: Run chart-testing (lint)
run: ct lint --chart-dirs .dist/wizard/config,.dist/wizard/config/apps,.dist/wizard/config/gpu --check-version-increment=false --all
# - name: Create kind cluster
# if: steps.list-changed.outputs.changed == 'true'
# uses: helm/kind-action@v1.7.0
# - name: Run chart-testing (install)
# if: steps.list-changed.outputs.changed == 'true'
# run: ct install --chart-dirs wizard/charts,wizard/config --target-branch ${{ github.event.repository.default_branch }}
test-version:
runs-on: ubuntu-latest
outputs:
@@ -107,12 +99,23 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- run: |
bash build/image-manifest.sh && bash build/upload-images.sh .manifest/images.mf
push-image-arm64:
@@ -132,12 +135,23 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Install coscmd
run: pip install coscmd
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- run: |
export PATH=$PATH:/usr/local/bin:/home/ubuntu/.local/bin
bash build/image-manifest.sh && bash build/upload-images.sh .manifest/images.mf linux/arm64
@@ -154,11 +168,23 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ needs.test-version.outputs.version }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
run: |
@@ -179,11 +205,21 @@ jobs:
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ needs.test-version.outputs.version }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
run: |
@@ -206,15 +242,28 @@ jobs:
run: |
bash build/build.sh ${{ needs.test-version.outputs.version }}
- name: Upload package
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload package
run: |
md5sum install-wizard-v${{ needs.test-version.outputs.version }}.tar.gz > install-wizard-v${{ needs.test-version.outputs.version }}.md5sum.txt && \
aws s3 cp install-wizard-v${{ needs.test-version.outputs.version }}.md5sum.txt s3://terminus-os-install/install-wizard-v${{ needs.test-version.outputs.version }}.md5sum.txt --acl=public-read && \
aws s3 cp install-wizard-v${{ needs.test-version.outputs.version }}.tar.gz s3://terminus-os-install/install-wizard-v${{ needs.test-version.outputs.version }}.tar.gz --acl=public-read
coscmd upload install-wizard-v${{ needs.test-version.outputs.version }}.md5sum.txt /install-wizard-v${{ needs.test-version.outputs.version }}.md5sum.txt && \
coscmd upload install-wizard-v${{ needs.test-version.outputs.version }}.tar.gz /install-wizard-v${{ needs.test-version.outputs.version }}.tar.gz
install-test:

View File

@@ -60,12 +60,24 @@ jobs:
OLARES_RELEASE_ID: ${{ inputs.release-id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to S3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload to CDN
run: |
cd cli/output && for file in $(ls *.tar.gz | grep -v no-release-id); do
aws s3 cp "$file" s3://terminus-os-install${{ secrets.REPO_PATH }}${file} --acl=public-read
coscmd upload "$file" ${{ secrets.REPO_PATH }}${file}
done

View File

@@ -72,12 +72,25 @@ jobs:
version: v1.18.2
args: release --clean
- name: Upload to CDN
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload to CDN
run: |
cd daemon/output && for file in $(ls *.tar.gz | grep -v no-release-id); do
aws s3 cp "$file" s3://terminus-os-install${{ secrets.REPO_PATH }}${file} --acl=public-read
coscmd upload "$file" ${{ secrets.REPO_PATH }}${file}
done

View File

@@ -55,11 +55,23 @@ jobs:
- name: 'Checkout source code'
uses: actions/checkout@v3
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- run: |
bash build/image-manifest.sh && bash build/upload-images.sh .manifest/images.mf
push-images-arm64:
@@ -69,11 +81,23 @@ jobs:
- name: 'Checkout source code'
uses: actions/checkout@v3
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- run: |
export PATH=$PATH:/usr/local/bin:/home/ubuntu/.local/bin
bash build/image-manifest.sh && bash build/upload-images.sh .manifest/images.mf linux/arm64
@@ -85,11 +109,24 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ needs.daily-version.outputs.version }}
RELEASE_ID: ${{ needs.release-id.outputs.id }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
@@ -104,11 +141,24 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ needs.daily-version.outputs.version }}
RELEASE_ID: ${{ needs.release-id.outputs.id }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
@@ -129,19 +179,31 @@ jobs:
run: |
bash build/build.sh ${{ needs.daily-version.outputs.version }} ${{ needs.release-id.outputs.id }}
- name: Upload to S3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload to COS
id: upload
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
run: |
md5sum install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz > install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt && \
aws s3 cp install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt --acl=public-read && \
aws s3 cp install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz --acl=public-read && \
coscmd upload install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt ${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt && \
coscmd upload install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz ${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz && \
aws s3 cp install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.${{ needs.release-id.outputs.id }}.md5sum.txt --acl=public-read && \
aws s3 cp install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.${{ needs.release-id.outputs.id }}.tar.gz --acl=public-read
coscmd upload install-wizard-v${{ needs.daily-version.outputs.version }}.md5sum.txt ${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.${{ needs.release-id.outputs.id }}.md5sum.txt && \
coscmd upload install-wizard-v${{ needs.daily-version.outputs.version }}.tar.gz ${{ secrets.REPO_PATH }}install-wizard-v${{ needs.daily-version.outputs.version }}.${{ needs.release-id.outputs.id }}.tar.gz
release:

View File

@@ -60,12 +60,24 @@ jobs:
args: release --clean --skip-validate -f .goreleaser.agent.yml
workdir: './daemon'
- name: Upload to CDN
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload to CDN
run: |
cd daemon/output && for file in *.tar.gz; do
aws s3 cp "$file" s3://terminus-os-install/$file --acl=public-read
coscmd upload "$file" ${{ secrets.REPO_PATH }}$file
done

View File

@@ -51,10 +51,23 @@ jobs:
with:
ref: ${{ github.event.inputs.tags }}
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
VERSION: ${{ github.event.inputs.tags }}
run: |
bash build/image-manifest.sh && bash build/upload-images.sh .manifest/images.mf
@@ -68,10 +81,24 @@ jobs:
with:
ref: ${{ github.event.inputs.tags }}
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
VERSION: ${{ github.event.inputs.tags }}
run: |
export PATH=$PATH:/usr/local/bin:/home/ubuntu/.local/bin
@@ -85,11 +112,24 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ github.event.inputs.tags }}
RELEASE_ID: ${{ needs.release-id.outputs.id }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
@@ -104,11 +144,25 @@ jobs:
- name: "Checkout source code"
uses: actions/checkout@v3
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
# test
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: "us-east-1"
VERSION: ${{ github.event.inputs.tags }}
RELEASE_ID: ${{ needs.release-id.outputs.id }}
REPO_PATH: '${{ secrets.REPO_PATH }}'
@@ -131,18 +185,30 @@ jobs:
run: |
bash build/build.sh ${{ github.event.inputs.tags }} ${{ needs.release-id.outputs.id }}
- name: Upload to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
- name: Install coscmd
run: pip install coscmd
- name: Configure coscmd
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
END_POINT: ${{ secrets.END_POINT }}
run: |
coscmd config -a $TENCENT_SECRET_ID \
-s $TENCENT_SECRET_KEY \
-b $COS_BUCKET \
-r $COS_REGION
- name: Upload to COS
run: |
md5sum install-wizard-v${{ github.event.inputs.tags }}.tar.gz > install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt && \
aws s3 cp install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt --acl=public-read && \
aws s3 cp install-wizard-v${{ github.event.inputs.tags }}.tar.gz s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.tar.gz --acl=public-read
coscmd upload install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt ${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt && \
coscmd upload install-wizard-v${{ github.event.inputs.tags }}.tar.gz ${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.tar.gz
aws s3 cp install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.${{ needs.release-id.outputs.id }}.md5sum.txt --acl=public-read && \
aws s3 cp install-wizard-v${{ github.event.inputs.tags }}.tar.gz s3://terminus-os-install${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.${{ needs.release-id.outputs.id }}.tar.gz --acl=public-read
coscmd upload install-wizard-v${{ github.event.inputs.tags }}.md5sum.txt ${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.${{ needs.release-id.outputs.id }}.md5sum.txt && \
coscmd upload install-wizard-v${{ github.event.inputs.tags }}.tar.gz ${{ secrets.REPO_PATH }}install-wizard-v${{ github.event.inputs.tags }}.${{ needs.release-id.outputs.id }}.tar.gz
release:
runs-on: ubuntu-latest

View File

@@ -3,10 +3,10 @@
# Olares: An Open-Source Personal Cloud to </br>Reclaim Your Data<!-- omit in toc -->
[![Mission](https://img.shields.io/badge/Mission-Let%20people%20own%20their%20data%20again-purple)](#)<br/>
[![Last Commit](https://img.shields.io/github/last-commit/beclab/olares)](https://github.com/beclab/olares/commits/main)
[![Last Commit](https://img.shields.io/github/last-commit/beclab/Olares)](https://github.com/beclab/olares/commits/main)
![Build Status](https://github.com/beclab/olares/actions/workflows/release-daily.yaml/badge.svg)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/beclab/olares)](https://github.com/beclab/olares/releases)
[![GitHub Repo stars](https://img.shields.io/github/stars/beclab/olares?style=social)](https://github.com/beclab/olares/stargazers)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/beclab/Olares)](https://github.com/beclab/olares/releases)
[![GitHub Repo stars](https://img.shields.io/github/stars/beclab/Olares?style=social)](https://github.com/beclab/Olares/stargazers)
[![Discord](https://img.shields.io/badge/Discord-7289DA?logo=discord&logoColor=white)](https://discord.gg/olares)
[![License](https://img.shields.io/badge/License-AGPL--3.0-blue)](https://github.com/beclab/olares/blob/main/LICENSE)
@@ -45,7 +45,7 @@ Just as Public clouds offer IaaS, PaaS, and SaaS layers, Olares provides open-so
![Tech Stacks](https://app.cdn.olares.com/github/olares/olares-architecture.jpg)
For detailed description of each component, refer to [Olares architecture](https://docs.olares.com/manual/concepts/system-architecture.html).
For detailed description of each component, refer to [Olares architecture](https://docs.olares.com/developer/concepts/system-architecture.html).
> 🔍 **How is Olares different from traditional NAS?**
>

View File

@@ -317,7 +317,7 @@ spec:
chown -R 1000:1000 /uploadstemp && \
chown -R 1000:1000 /appdata
- name: olares-app-init
image: beclab/system-frontend:v1.9.6
image: beclab/system-frontend:v1.9.9
imagePullPolicy: IfNotPresent
command:
- /bin/sh

View File

@@ -27,4 +27,5 @@ mkdir redis-5.0.14 && \
cp /usr/local/bin/redis* ./redis-5.0.14/
tar czvf ./redis-5.0.14.tar.gz ./redis-5.0.14/ && \
aws s3 cp redis-5.0.14.tar.gz s3://terminus-os-install/redis-5.0.14_${os}_${arch}${SUFFIX}.tar.gz --acl=public-read
# aws s3 cp redis-5.0.14.tar.gz s3://terminus-os-install/redis-5.0.14_${os}_${arch}${SUFFIX}.tar.gz --acl=public-read
coscmd upload redis-5.0.14.tar.gz /redis-5.0.14_${os}_${arch}${SUFFIX}.tar.gz --acl=public-read

View File

@@ -6,4 +6,5 @@ set -xe
curl -Lo Ubuntu2204.appx https://wslstorestorage.blob.core.windows.net/wslblob/Ubuntu2204-221101.AppxBundle
ubuntu2204=$(md5sum Ubuntu2204.appx|awk '{print $1}')
aws s3 cp Ubuntu2204.appx s3://terminus-os-install/${ubuntu2204} --acl=public-read
# aws s3 cp Ubuntu2204.appx s3://terminus-os-install/${ubuntu2204} --acl=public-read
coscmd upload Ubuntu2204.appx /${ubuntu2204} --acl=public-read

View File

@@ -6,9 +6,11 @@ set -xe
curl -Lo wsl.2.3.26.0.amd64.msi https://github.com/microsoft/WSL/releases/download/2.3.26/wsl.2.3.26.0.x64.msi
wsl_2_3_26=$(md5sum wsl.2.3.26.0.amd64.msi|awk '{print $1}')
aws s3 cp wsl.2.3.26.0.amd64.msi s3://terminus-os-install/${wsl_2_3_26} --acl=public-read
# aws s3 cp wsl.2.3.26.0.amd64.msi s3://terminus-os-install/${wsl_2_3_26} --acl=public-read
coscmd upload wsl.2.3.26.0.amd64.msi /${wsl_2_3_26} --acl=public-read
curl -Lo wsl.2.3.26.0.arm64.msi https://github.com/microsoft/WSL/releases/download/2.3.26/wsl.2.3.26.0.arm64.msi
wsl_2_3_26_arm64=$(md5sum wsl.2.3.26.0.arm64.msi|awk '{print $1}')
aws s3 cp wsl.2.3.26.0.arm64.msi s3://terminus-os-install/arm64/${wsl_2_3_26_arm64} --acl=public-read
# aws s3 cp wsl.2.3.26.0.arm64.msi s3://terminus-os-install/arm64/${wsl_2_3_26_arm64} --acl=public-read
coscmd upload wsl.2.3.26.0.arm64.msi /arm64/${wsl_2_3_26_arm64} --acl=public-read

View File

@@ -31,7 +31,7 @@ while read line; do
curl -fsSLI https://cdn.olares.com/$path$name > /dev/null
if [ $? -ne 0 ]; then
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://cdn.olares.com/$path$name)
if [ $code -eq 403 ]; then
if [[ $code -eq 403 || $code -eq 404 ]]; then
bash ${BASE_DIR}/download-deps.sh $PLATFORM $line
if [ $? -ne 0 ]; then
@@ -46,28 +46,25 @@ while read line; do
fi
set -ex
aws s3 cp $name s3://terminus-os-install/$path$name --acl=public-read
aws s3 cp $name s3://terminus-os-install/backup/$path$backup_file --acl=public-read
aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
echo "upload $name to s3 completed"
# aws s3 cp $name s3://terminus-os-install/$path$name --acl=public-read
# aws s3 cp $name s3://terminus-os-install/backup/$path$backup_file --acl=public-read
# aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
# echo "upload $name to s3 completed"
coscmd upload ./$name /$path$name
coscmd upload ./$name /backup/$path$backup_file
coscmd upload ./$checksum /$path$checksum
echo "upload $name to cos completed"
set +ex
else
if [ $code -ne 200 ]; then
echo "failed to check image"
echo "failed to check file"
exit -1
fi
fi
fi
# upload to tencent cloud cos
# curl -fsSLI https://cdn.joinolares.cn/$path$name > /dev/null
# if [ $? -ne 0 ]; then
# set -ex
# coscmd upload ./$name /$path$name
# coscmd upload ./$checksum /$path$checksum
# echo "upload $name to cos completed"
# set +ex
# fi
done < components
popd

View File

@@ -15,7 +15,7 @@ cat $1|while read image; do
curl -fsSLI https://cdn.olares.com/$path$name.tar.gz > /dev/null
if [ $? -ne 0 ]; then
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://cdn.olares.com/$path$name.tar.gz)
if [ $code -eq 403 ]; then
if [[ $code -eq 403 || $code -eq 404 ]]; then
set -ex
skopeo copy --insecure-policy docker://$image oci-archive:$name.tar
gzip $name.tar
@@ -28,11 +28,16 @@ cat $1|while read image; do
fi
echo "start to upload [$name.tar.gz]"
aws s3 cp $name.tar.gz s3://terminus-os-install/$path$name.tar.gz --acl=public-read
aws s3 cp $name.tar.gz s3://terminus-os-install/backup/$path$backup_file --acl=public-read
aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
echo "upload $name completed"
# aws s3 cp $name.tar.gz s3://terminus-os-install/$path$name.tar.gz --acl=public-read
# aws s3 cp $name.tar.gz s3://terminus-os-install/backup/$path$backup_file --acl=public-read
# aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
# echo "upload $name completed"
coscmd upload ./$name.tar.gz /$path$name.tar.gz
coscmd upload ./$name.tar.gz /backup/$path$backup_file
coscmd upload ./$checksum /$path$checksum
echo "upload $name to cos completed"
set +ex
else
if [ $code -ne 200 ]; then
@@ -48,7 +53,7 @@ cat $1|while read image; do
curl -fsSLI https://cdn.olares.com/$path$checksum > /dev/null
if [ $? -ne 0 ]; then
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://cdn.olares.com/$path$checksum)
if [ $code -eq 403 ]; then
if [[ $code -eq 403 || $code -eq 404 ]]; then
set -ex
skopeo copy --insecure-policy docker://$image oci-archive:$name.tar
gzip $name.tar
@@ -60,10 +65,16 @@ cat $1|while read image; do
exit 1
fi
aws s3 cp $name.tar.gz s3://terminus-os-install/$path$name.tar.gz --acl=public-read
aws s3 cp $name.tar.gz s3://terminus-os-install/backup/$path$backup_file --acl=public-read
aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
echo "upload $name completed"
# aws s3 cp $name.tar.gz s3://terminus-os-install/$path$name.tar.gz --acl=public-read
# aws s3 cp $name.tar.gz s3://terminus-os-install/backup/$path$backup_file --acl=public-read
# aws s3 cp $checksum s3://terminus-os-install/$path$checksum --acl=public-read
# echo "upload $name completed"
coscmd upload ./$name.tar.gz /$path$name.tar.gz
coscmd upload ./$name.tar.gz /backup/$path$backup_file
coscmd upload ./$checksum /$path$checksum
echo "upload $name to cos completed"
set +ex
else
if [ $code -ne 200 ]; then
@@ -77,13 +88,16 @@ cat $1|while read image; do
curl -fsSLI https://cdn.olares.com/$path$manifest > /dev/null
if [ $? -ne 0 ]; then
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://cdn.olares.com/$path$manifest)
if [ $code -eq 403 ]; then
if [[ $code -eq 403 || $code -eq 404 ]]; then
set -ex
BASE_DIR=$(dirname $(realpath -s $0))
python3 $BASE_DIR/get-manifest.py $image -o $manifest
aws s3 cp $manifest s3://terminus-os-install/$path$manifest --acl=public-read
# aws s3 cp $manifest s3://terminus-os-install/$path$manifest --acl=public-read
coscmd upload $manifest /$path$manifest
echo "upload $name manifest completed"
set +ex
else
if [ $code -ne 200 ]; then

121
cli/pkg/amdgpu/module.go Normal file
View File

@@ -0,0 +1,121 @@
package amdgpu
import (
"time"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/prepare"
"github.com/beclab/Olares/cli/pkg/core/task"
)
// InstallAmdContainerToolkitModule installs AMD container toolkit on supported Ubuntu if ROCm is installed.
type InstallAmdContainerToolkitModule struct {
common.KubeModule
Skip bool // conditional execution based on ROCm detection
SkipRocmCheck bool
}
func (m *InstallAmdContainerToolkitModule) IsSkip() bool {
return m.Skip
}
func (m *InstallAmdContainerToolkitModule) Init() {
m.Name = "InstallAmdContainerToolkit"
if m.IsSkip() {
return
}
prepareCollection := prepare.PrepareCollection{}
if !m.SkipRocmCheck {
prepareCollection = append(prepareCollection, new(RocmInstalled))
}
updateAmdSource := &task.RemoteTask{
Name: "UpdateAmdContainerToolkitSource",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Action: new(UpdateAmdContainerToolkitSource),
Prepare: &prepareCollection,
Parallel: false,
Retry: 1,
}
installAmdContainerToolkit := &task.RemoteTask{
Name: "InstallAmdContainerToolkit",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Prepare: &prepareCollection,
Action: new(InstallAmdContainerToolkit),
Parallel: false,
Retry: 1,
}
generateAndValidateCDI := &task.RemoteTask{
Name: "GenerateAndValidateAmdCDI",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Prepare: &prepareCollection,
Action: new(GenerateAndValidateAmdCDI),
Parallel: false,
Retry: 1,
}
m.Tasks = []task.Interface{
updateAmdSource,
installAmdContainerToolkit,
generateAndValidateCDI,
}
}
// InstallAmdPluginModule installs AMD GPU device plugin on Kubernetes.
type InstallAmdPluginModule struct {
common.KubeModule
Skip bool // conditional execution based on GPU enablement
}
func (m *InstallAmdPluginModule) IsSkip() bool {
return m.Skip
}
func (m *InstallAmdPluginModule) Init() {
m.Name = "InstallAmdPlugin"
// update node with AMD GPU labels
updateNode := &task.RemoteTask{
Name: "UpdateNodeAmdGPUInfo",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
},
Action: new(UpdateNodeAmdGPUInfo),
Parallel: false,
Retry: 1,
}
installPlugin := &task.RemoteTask{
Name: "InstallAmdPlugin",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
},
Action: new(InstallAmdPlugin),
Parallel: false,
Retry: 1,
}
checkGpuState := &task.RemoteTask{
Name: "CheckAmdGPUState",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
new(RocmInstalled),
},
Action: new(CheckAmdGpuStatus),
Parallel: false,
Retry: 50,
Delay: 10 * time.Second,
}
m.Tasks = []task.Interface{
updateNode,
installPlugin,
checkGpuState,
}
}

View File

@@ -0,0 +1,56 @@
package amdgpu
import (
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
)
// RocmInstalled checks if AMD ROCm is installed on the system.
type RocmInstalled struct {
common.KubePrepare
}
func (p *RocmInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
rocmV, err := connector.RocmVersion()
if err != nil {
logger.Debugf("ROCm version check error: %v", err)
return false, nil
}
if rocmV == nil {
return false, nil
}
logger.Infof("Detected ROCm version: %s", rocmV.Original())
return true, nil
}
// RocmNotInstalled checks if AMD ROCm is NOT installed on the system.
type RocmNotInstalled struct {
common.KubePrepare
RocmInstalled
}
func (p *RocmNotInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
installed, err := p.RocmInstalled.PreCheck(runtime)
if err != nil {
return false, err
}
return !installed, nil
}
// ContainerdInstalled checks if containerd is installed on the system.
type ContainerdInstalled struct {
common.KubePrepare
}
func (p *ContainerdInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
containerdCheck := precheck.ConflictingContainerdCheck{}
if err := containerdCheck.Check(runtime); err != nil {
return true, nil
}
logger.Info("containerd is not installed, ignore task")
return false, nil
}

View File

@@ -1,17 +1,20 @@
package amdgpu
import (
"context"
"fmt"
"os/exec"
"path"
"path/filepath"
"github.com/beclab/Olares/cli/pkg/clientset"
"github.com/beclab/Olares/cli/pkg/common"
cc "github.com/beclab/Olares/cli/pkg/core/common"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/utils"
"github.com/beclab/Olares/cli/pkg/core/util"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
@@ -26,8 +29,8 @@ func (m *InstallAmdRocmModule) Init() {
m.Name = "InstallAMDGPU"
installAmd := &task.RemoteTask{
Name: "InstallAmdRocm",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Name: "InstallAmdRocm",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Action: &InstallAmdRocm{
// no manifest needed
},
@@ -51,7 +54,7 @@ func (t *InstallAmdRocm) Execute(runtime connector.Runtime) error {
return nil
}
amdGPUExists, err := utils.HasAmdIGPU(runtime)
amdGPUExists, err := connector.HasAmdAPUOrGPU(runtime)
if err != nil {
return err
}
@@ -59,7 +62,7 @@ func (t *InstallAmdRocm) Execute(runtime connector.Runtime) error {
if !amdGPUExists {
return nil
}
rocmV, _ := utils.RocmVersion()
rocmV, _ := connector.RocmVersion()
min := semver.MustParse("7.1.1")
if rocmV != nil && rocmV.LessThan(min) {
return fmt.Errorf("detected ROCm version %s, which is lower than required %s; please uninstall existing ROCm/AMDGPU components before installation with command: olares-cli amdgpu uninstall", rocmV.Original(), min.Original())
@@ -131,3 +134,163 @@ func (t *AmdgpuUninstallAction) Execute(runtime connector.Runtime) error {
logger.Warn("Warning: Please reboot your machine after uninstall to fully remove ROCm components.")
return nil
}
// UpdateAmdContainerToolkitSource configures the AMD container toolkit APT repository.
type UpdateAmdContainerToolkitSource struct {
common.KubeAction
}
func (t *UpdateAmdContainerToolkitSource) Execute(runtime connector.Runtime) error {
// Install prerequisites
if _, err := runtime.GetRunner().SudoCmd("apt update && apt install -y wget gnupg2", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install prerequisites for AMD container toolkit")
}
if _, err := runtime.GetRunner().SudoCmd("install -d -m 0755 /etc/apt/keyrings", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to create /etc/apt/keyrings directory")
}
cmd := "wget https://repo.radeon.com/rocm/rocm.gpg.key -O - | gpg --dearmor | tee /etc/apt/keyrings/rocm.gpg > /dev/null"
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to download and install AMD ROCm GPG key")
}
si := runtime.GetSystemInfo()
var ubuntuCodename string
if si.IsUbuntuVersionEqual(connector.Ubuntu2404) {
ubuntuCodename = "noble"
} else if si.IsUbuntuVersionEqual(connector.Ubuntu2204) {
ubuntuCodename = "jammy"
} else {
return fmt.Errorf("unsupported Ubuntu version for AMD container toolkit")
}
aptSourceLine := fmt.Sprintf("deb [signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/amd-container-toolkit/apt/ %s main", ubuntuCodename)
cmd = fmt.Sprintf("echo '%s' > /etc/apt/sources.list.d/amd-container-toolkit.list", aptSourceLine)
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to add AMD container toolkit APT source")
}
logger.Infof("AMD container toolkit repository configured successfully")
return nil
}
// InstallAmdContainerToolkit installs the AMD container toolkit package.
type InstallAmdContainerToolkit struct {
common.KubeAction
}
func (t *InstallAmdContainerToolkit) Execute(runtime connector.Runtime) error {
logger.Infof("Installing AMD container toolkit...")
if _, err := runtime.GetRunner().SudoCmd("apt update && apt install -y amd-container-toolkit", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install AMD container toolkit")
}
logger.Infof("AMD container toolkit installed successfully")
return nil
}
// GenerateAndValidateAmdCDI generates and validates the AMD CDI spec.
type GenerateAndValidateAmdCDI struct {
common.KubeAction
}
func (t *GenerateAndValidateAmdCDI) Execute(runtime connector.Runtime) error {
// Ensure /etc/cdi directory exists
if _, err := runtime.GetRunner().SudoCmd("install -d -m 0755 /etc/cdi", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to create /etc/cdi directory")
}
// Generate CDI spec
logger.Infof("Generating AMD CDI spec...")
if _, err := runtime.GetRunner().SudoCmd("amd-ctk cdi generate --output=/etc/cdi/amd.json", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to generate AMD CDI spec")
}
// Validate CDI spec
logger.Infof("Validating AMD CDI spec...")
if _, err := runtime.GetRunner().SudoCmd("amd-ctk cdi validate --path=/etc/cdi/amd.json", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to validate AMD CDI spec")
}
logger.Infof("AMD CDI spec generated and validated successfully")
return nil
}
// UpdateNodeAmdGPUInfo updates Kubernetes node labels with AMD GPU information.
type UpdateNodeAmdGPUInfo struct {
common.KubeAction
}
func (u *UpdateNodeAmdGPUInfo) Execute(runtime connector.Runtime) error {
client, err := clientset.NewKubeClient()
if err != nil {
return errors.Wrap(errors.WithStack(err), "kubeclient create error")
}
// Check if AMD GPU/APU exists
amdGPUExists, err := connector.HasAmdAPUOrGPU(runtime)
if err != nil {
return err
}
if !amdGPUExists {
logger.Info("AMD GPU/APU is not detected")
return nil
}
// Get ROCm version
rocmV, err := connector.RocmVersion()
if err != nil || rocmV == nil {
logger.Info("ROCm is not installed")
return nil
}
rocmVersion := rocmV.Original()
// Determine GPU type (APU vs discrete GPU)
gpuType := gpu.AmdGpuCardType
if runtime.GetSystemInfo().IsAmdApu() {
gpuType = gpu.AmdApuCardType
}
// Use ROCm version as both driver and "cuda" version for AMD
return gpu.UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), &rocmVersion, nil, nil, &gpuType)
}
// InstallAmdPlugin installs the AMD GPU device plugin DaemonSet.
type InstallAmdPlugin struct {
common.KubeAction
}
func (t *InstallAmdPlugin) Execute(runtime connector.Runtime) error {
amdPluginPath := path.Join(runtime.GetInstallerDir(), "wizard/config/gpu/nvidia/amdgpu-device-plugin.yaml")
_, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("kubectl apply -f %s", amdPluginPath), false, true)
if err != nil {
return errors.Wrap(errors.WithStack(err), "failed to apply AMD GPU device plugin")
}
logger.Infof("AMD GPU device plugin installed successfully")
return nil
}
// CheckAmdGpuStatus checks if the AMD GPU device plugin pod is running.
type CheckAmdGpuStatus struct {
common.KubeAction
}
func (t *CheckAmdGpuStatus) Execute(runtime connector.Runtime) error {
kubectlpath, err := util.GetCommand(common.CommandKubectl)
if err != nil {
return fmt.Errorf("kubectl not found")
}
// Check AMD device plugin pod status using the label from amdgpu-device-plugin.yaml
selector := "name=amdgpu-dp-ds"
cmd := fmt.Sprintf("%s get pod -n kube-system -l '%s' -o jsonpath='{.items[*].status.phase}'", kubectlpath, selector)
rphase, _ := runtime.GetRunner().SudoCmd(cmd, false, false)
if rphase == "Running" {
logger.Infof("AMD GPU device plugin is running")
return nil
}
return fmt.Errorf("AMD GPU device plugin state is not Running (current: %s)", rphase)
}

View File

@@ -59,7 +59,7 @@ func (t *PatchTask) Execute(runtime connector.Runtime) error {
pre_reqs = pre_reqs + " network-manager "
}
pre_reqs += " conntrack socat apache2-utils net-tools make gcc bison flex tree unzip "
pre_reqs += " conntrack socat apache2-utils net-tools make gcc bison flex tree unzip lshw"
var systemInfo = runtime.GetSystemInfo()
var platformFamily = systemInfo.GetOsPlatformFamily()

View File

@@ -338,7 +338,9 @@ func (c *CudaChecker) Name() string {
}
func (c *CudaChecker) Check(runtime connector.Runtime) error {
if !runtime.GetSystemInfo().IsLinux() {
if !runtime.GetSystemInfo().IsLinux() ||
// Skip check on NVIDIA DGX Spark systems, which have their own GPU management
runtime.GetSystemInfo().IsGB10Chip() {
return nil
}
@@ -388,17 +390,17 @@ func (r *RocmChecker) Check(runtime connector.Runtime) error {
return nil
}
// detect AMD GPU presence
amdGPUExists, err := utils.HasAmdIGPU(runtime)
// detect AMD APU/GPU presence
amdGPUExists, err := connector.HasAmdAPUOrGPU(runtime)
if err != nil {
return err
}
// no AMD GPU found, no need to check rocm
// no AMD APU/GPU found, no need to check rocm
if !amdGPUExists {
return nil
}
curV, err := utils.RocmVersion()
curV, err := connector.RocmVersion()
if err != nil && !os.IsNotExist(err) {
return err
}

View File

@@ -159,6 +159,7 @@ const (
CommandUpdatePciids = "update-pciids"
CommandNmcli = "nmcli"
CommandZRAMCtl = "zramctl"
CommandChronyc = "chronyc"
CacheCommandKubectlPath = "kubectl_bin_path"
CacheCommandMinikubePath = "minikube_bin_path"

View File

@@ -210,6 +210,7 @@ func NewArgument() *Argument {
arg.IsCloudInstance, _ = strconv.ParseBool(os.Getenv(ENV_TERMINUS_IS_CLOUD_VERSION))
arg.IsOlaresInContainer = os.Getenv(ENV_CONTAINER_MODE) == "oic"
si.IsOIC = arg.IsOlaresInContainer
si.ProductName = arg.GetProductName()
// Ensure BaseDir is initialized before loading master.conf
// so master host config can be loaded from ${base-dir}/master.conf reliably.
@@ -415,6 +416,57 @@ func (a *Argument) SetSwapConfig(config SwapConfig) {
a.Swappiness = config.Swappiness
}
func (a *Argument) SetMasterHostOverride(config MasterHostConfig) {
if config.MasterHost != "" {
a.MasterHost = config.MasterHost
}
if config.MasterNodeName != "" {
a.MasterNodeName = config.MasterNodeName
}
// set a dummy name to bypass validity checks
// as it will be overridden later when the node name is fetched
if a.MasterNodeName == "" {
a.MasterNodeName = "master"
}
if config.MasterSSHPassword != "" {
a.MasterSSHPassword = config.MasterSSHPassword
}
if config.MasterSSHUser != "" {
a.MasterSSHUser = config.MasterSSHUser
}
if config.MasterSSHPort != 0 {
a.MasterSSHPort = config.MasterSSHPort
}
if config.MasterSSHPrivateKeyPath != "" {
a.MasterSSHPrivateKeyPath = config.MasterSSHPrivateKeyPath
}
}
func (a *Argument) LoadMasterHostConfigIfAny() error {
if a.BaseDir == "" {
return errors.New("basedir unset")
}
content, err := os.ReadFile(filepath.Join(a.BaseDir, MasterHostConfigFile))
if os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
return json.Unmarshal(content, a.MasterHostConfig)
}
func (a *Argument) GetProductName() string {
data, err := os.ReadFile("/sys/class/dmi/id/product_name")
if err != nil {
fmt.Printf("\nCannot get product name on this device, %s\n", err)
return ""
}
return strings.TrimSpace(string(data))
}
func NewKubeRuntime(arg Argument) (*KubeRuntime, error) {
loader := NewLoader(arg)
cluster, err := loader.Load()

View File

@@ -98,4 +98,6 @@ const (
const (
ZfsSnapshotter = "/var/lib/containerd/io.containerd.snapshotter.v1.zfs"
ENV_GB10_CHIP = "GB10_CHIP" // for building images for NVIDIA GB10 Superchip systems
)

View File

@@ -0,0 +1,117 @@
package connector
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/Masterminds/semver/v3"
)
func hasAmdAPU(cmdExec func(s string) (string, error)) (bool, error) {
// Detect by CPU model names that bundle AMD AI NPU/graphics
targets := []string{
"AMD Ryzen AI Max+ 395",
"AMD Ryzen AI Max 390",
"AMD Ryzen AI Max 385",
"AMD Ryzen AI 9 HX 375",
"AMD Ryzen AI 9 HX 370",
"AMD Ryzen AI 9 365",
}
// try lscpu first: extract 'Model name' field
out, err := cmdExec("lscpu 2>/dev/null | awk -F': *' '/^Model name/{print $2; exit}' || true")
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
// fallback to /proc/cpuinfo
out, err = cmdExec("awk -F': *' '/^model name/{print $2; exit}' /proc/cpuinfo 2>/dev/null || true")
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
return false, nil
}
func hasAmdAPUOrGPU(cmdExec func(s string) (string, error)) (bool, error) {
out, err := cmdExec("lspci -d '1002:' 2>/dev/null | grep 'AMD' || true")
if err != nil {
return false, err
}
if out != "" {
return true, nil
}
out, err = cmdExec("lshw -c display -numeric -disable network 2>/dev/null | grep 'vendor: .* \\[1002\\]' || true")
if err != nil {
return false, err
}
if out != "" {
return true, nil
}
return false, nil
}
func HasAmdAPU(execRuntime Runtime) (bool, error) {
return hasAmdAPU(func(s string) (string, error) {
return execRuntime.GetRunner().SudoCmd(s, false, false)
})
}
func HasAmdAPULocal() (bool, error) {
return hasAmdAPU(func(s string) (string, error) {
out, err := exec.Command("sh", "-c", s).Output()
if err != nil {
return "", err
}
return string(out), nil
})
}
func HasAmdAPUOrGPULocal() (bool, error) {
return hasAmdAPUOrGPU(func(s string) (string, error) {
out, err := exec.Command("sh", "-c", s).Output()
if err != nil {
return "", err
}
return string(out), nil
})
}
func HasAmdAPUOrGPU(execRuntime Runtime) (bool, error) {
return hasAmdAPUOrGPU(func(s string) (string, error) {
return execRuntime.GetRunner().SudoCmd(s, false, false)
})
}
func RocmVersion() (*semver.Version, error) {
const rocmVersionFile = "/opt/rocm/.info/version"
data, err := os.ReadFile(rocmVersionFile)
if err != nil {
// no ROCm installed, nothing to check
if os.IsNotExist(err) {
return nil, err
}
return nil, err
}
curStr := strings.TrimSpace(string(data))
cur, err := semver.NewVersion(curStr)
if err != nil {
return nil, fmt.Errorf("invalid rocm version: %s", curStr)
}
return cur, nil
}

View File

@@ -76,6 +76,10 @@ type Systems interface {
IsPveOrPveLxc() bool
IsRaspbian() bool
IsLinux() bool
IsGB10Chip() bool
IsAmdApu() bool
IsAmdGPU() bool
IsAmdGPUOrAPU() bool
IsUbuntu() bool
IsDebian() bool
@@ -111,16 +115,18 @@ type Systems interface {
}
type SystemInfo struct {
HostInfo *HostInfo `json:"host"`
CpuInfo *CpuInfo `json:"cpu"`
DiskInfo *DiskInfo `json:"disk"`
MemoryInfo *MemoryInfo `json:"memory"`
FsInfo *FileSystemInfo `json:"filesystem"`
CgroupInfo *CgroupInfo `json:"cgroup,omitempty"`
LocalIp string `json:"local_ip"`
NatGateway string `json:"nat_gateway"`
PkgManager string `json:"pkg_manager"`
IsOIC bool `json:"is_oic,omitempty"`
HostInfo *HostInfo `json:"host"`
CpuInfo *CpuInfo `json:"cpu"`
DiskInfo *DiskInfo `json:"disk"`
MemoryInfo *MemoryInfo `json:"memory"`
FsInfo *FileSystemInfo `json:"filesystem"`
CgroupInfo *CgroupInfo `json:"cgroup,omitempty"`
LocalIp string `json:"local_ip"`
NatGateway string `json:"nat_gateway"`
PkgManager string `json:"pkg_manager"`
IsOIC bool `json:"is_oic,omitempty"`
ProductName string `json:"product_name,omitempty"`
HasAmdGPU bool `json:"has_amd_gpu,omitempty"`
}
func (s *SystemInfo) IsSupport() error {
@@ -235,6 +241,22 @@ func (s *SystemInfo) IsLinux() bool {
return s.HostInfo.OsType == common.Linux
}
func (s *SystemInfo) IsGB10Chip() bool {
return s.CpuInfo.IsGB10Chip
}
func (s *SystemInfo) IsAmdApu() bool {
return s.CpuInfo.HasAmdAPU
}
func (s *SystemInfo) IsAmdGPU() bool {
return s.HasAmdGPU
}
func (s *SystemInfo) IsAmdGPUOrAPU() bool {
return s.CpuInfo.HasAmdAPU || s.HasAmdGPU
}
func (s *SystemInfo) IsUbuntu() bool {
return s.HostInfo.OsPlatformFamily == common.Ubuntu
}
@@ -322,6 +344,12 @@ func GetSystemInfo() *SystemInfo {
si.MemoryInfo = getMem()
si.FsInfo = getFs()
hasAmdGPU, err := getAmdGPU()
if err != nil {
panic(errors.Wrap(err, "failed to get amd apu/gpu"))
}
si.HasAmdGPU = hasAmdGPU
localIP, err := util.GetLocalIP()
if err != nil {
panic(errors.Wrap(err, "failed to get local ip"))
@@ -437,6 +465,28 @@ type CpuInfo struct {
CpuModel string `json:"cpu_model"`
CpuLogicalCount int `json:"cpu_logical_count"`
CpuPhysicalCount int `json:"cpu_physical_count"`
IsGB10Chip bool `json:"is_gb10_chip,omitempty"`
HasAmdAPU bool `json:"has_amd_apu,omitempty"`
}
// Not considering the case where AMD GPU and AMD APU coexist.
func getAmdGPU() (bool, error) {
APUOrGPUExists, err := HasAmdAPUOrGPULocal()
if err != nil {
fmt.Printf("Error checking AMD APU/GPU: %v\n", err)
return false, err
}
hasAmdAPU, err := HasAmdAPULocal()
if err != nil {
fmt.Printf("Error checking AMD APU: %v\n", err)
return false, err
}
if APUOrGPUExists && !hasAmdAPU {
return true, nil
}
return false, nil
}
func getCpu() *CpuInfo {
@@ -452,10 +502,36 @@ func getCpu() *CpuInfo {
cpuModel = cpuInfo[0].ModelName
}
// check if is GB10 chip
isGB10Chip := false
// In Linux systems, it is recognized via lspci as "NVIDIA Corporation Device 2e12 (rev a1)
// or NVIDIA Corporation GB20B [GB10] (rev a1)
cmd := exec.Command("sh", "-c", "lspci | grep -i vga | egrep 'GB10|2e12'")
output, err := cmd.Output()
if err == nil && strings.TrimSpace(string(output)) != "" {
isGB10Chip = true
} else {
fmt.Printf("Error checking GB10 chip: %v\n", err)
gb10env := os.Getenv(common.ENV_GB10_CHIP)
if gb10env == "1" || strings.EqualFold(gb10env, "true") {
isGB10Chip = true
}
}
// check if it has amd igpu
hasAmdAPU, err := HasAmdAPULocal()
if err != nil {
fmt.Printf("Error checking AMD iGPU: %v\n", err)
hasAmdAPU = false
}
return &CpuInfo{
CpuModel: cpuModel,
CpuLogicalCount: cpuLogicalCount,
CpuPhysicalCount: cpuPhysicalCount,
IsGB10Chip: isGB10Chip,
HasAmdAPU: hasAmdAPU,
}
}

View File

@@ -27,7 +27,7 @@ var (
ETCDService = template.Must(template.New("etcd.service").Parse(
dedent.Dedent(`[Unit]
Description=etcd
After=network.target
After=network-online.target
StartLimitIntervalSec=0
[Service]

View File

@@ -37,6 +37,11 @@ type CudaInstalled struct {
}
func (p *CudaInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
if runtime.GetSystemInfo().IsGB10Chip() {
logger.Debug("Assume DGX Spark or GB10 OEM system has CUDA installed")
return true, nil
}
st, err := utils.GetNvidiaStatus(runtime)
if err != nil {
return false, err
@@ -50,17 +55,15 @@ func (p *CudaInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
type CudaNotInstalled struct {
common.KubePrepare
CudaInstalled
}
func (p *CudaNotInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
st, err := utils.GetNvidiaStatus(runtime)
installed, err := p.CudaInstalled.PreCheck(runtime)
if err != nil {
return false, err
}
if st == nil || !st.Installed {
return true, nil
}
return false, nil
return !installed, nil
}
type CurrentNodeInK8s struct {

View File

@@ -325,7 +325,8 @@ func (t *CheckGpuStatus) Execute(runtime connector.Runtime) error {
return fmt.Errorf("kubectl not found")
}
cmd := fmt.Sprintf("%s get pod -n kube-system -l 'app.kubernetes.io/component=hami-device-plugin' -o jsonpath='{.items[*].status.phase}'", kubectlpath)
selector := "app.kubernetes.io/component=hami-device-plugin"
cmd := fmt.Sprintf("%s get pod -n kube-system -l '%s' -o jsonpath='{.items[*].status.phase}'", kubectlpath, selector)
rphase, _ := runtime.GetRunner().SudoCmd(cmd, false, false)
if rphase == "Running" {
@@ -363,7 +364,16 @@ func (u *UpdateNodeGPUInfo) Execute(runtime connector.Runtime) error {
driverVersion = st.LibraryVersion
}
return UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), &driverVersion, &st.CudaVersion, &supported)
// TODO:
gpuType := NvidiaCardType
switch {
case runtime.GetSystemInfo().IsAmdApu():
gpuType = AmdApuCardType
case runtime.GetSystemInfo().IsGB10Chip():
gpuType = GB10ChipType
}
return UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), &driverVersion, &st.CudaVersion, &supported, &gpuType)
}
type RemoveNodeLabels struct {
@@ -376,12 +386,12 @@ func (u *RemoveNodeLabels) Execute(runtime connector.Runtime) error {
return errors.Wrap(errors.WithStack(err), "kubeclient create error")
}
return UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), nil, nil, nil)
return UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), nil, nil, nil, nil)
}
// update k8s node labels gpu.bytetrade.io/driver and gpu.bytetrade.io/cuda.
// if labels are not exists, create it.
func UpdateNodeGpuLabel(ctx context.Context, client kubernetes.Interface, driver, cuda *string, supported *string) error {
func UpdateNodeGpuLabel(ctx context.Context, client kubernetes.Interface, driver, cuda *string, supported *string, gpuType *string) error {
// get node name from hostname
nodeName, err := os.Hostname()
if err != nil {
@@ -408,6 +418,7 @@ func UpdateNodeGpuLabel(ctx context.Context, client kubernetes.Interface, driver
{GpuDriverLabel, driver},
{GpuCudaLabel, cuda},
{GpuCudaSupportedLabel, supported},
{GpuType, gpuType},
} {
old, ok := labels[label.key]
switch {

View File

@@ -8,4 +8,13 @@ var (
GpuDriverLabel = GpuLabelGroup + "/driver"
GpuCudaLabel = GpuLabelGroup + "/cuda"
GpuCudaSupportedLabel = GpuLabelGroup + "/cuda-supported"
GpuType = GpuLabelGroup + "/type"
)
const (
NvidiaCardType = "nvidia" // handling by HAMi
AmdGpuCardType = "amd-gpu" //
AmdApuCardType = "amd-apu" // AMD APU with integrated GPU , AI Max 395 etc.
GB10ChipType = "nvidia-gb10" // NVIDIA GB10 Superchip & unified system memory
StrixHaloChipType = "strix-halo" // AMD Strix Halo GPU & unified system memory
)

View File

@@ -1,6 +1,7 @@
package cluster
import (
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/gpu"
@@ -58,6 +59,12 @@ func (l *linuxInstallPhaseBuilder) installGpuPlugin() phase {
return []module.Module{
&gpu.RestartK3sServiceModule{Skip: !(l.runtime.Arg.Kubetype == common.K3s)},
&gpu.InstallPluginModule{Skip: skipGpuPlugin},
&amdgpu.InstallAmdPluginModule{Skip: func() bool {
if l.runtime.GetSystemInfo().IsAmdGPUOrAPU() {
return false
}
return true
}()},
}
}

View File

@@ -83,6 +83,13 @@ func (l *linuxPhaseBuilder) build() []module.Module {
addModule(gpuModuleBuilder(func() []module.Module {
return []module.Module{
&amdgpu.InstallAmdRocmModule{},
&amdgpu.InstallAmdContainerToolkitModule{Skip: func() bool {
if l.runtime.GetSystemInfo().IsAmdGPUOrAPU() {
return false
}
return true
}(),
},
&gpu.InstallDriversModule{
ManifestModule: manifest.ManifestModule{
Manifest: l.manifestMap,

View File

@@ -116,8 +116,14 @@ func (m *CheckPreparedModule) Init() {
Action: &CheckPrepared{Force: m.Force},
}
checkTimeSync := &task.LocalTask{
Name: "CheckTimeSynced",
Action: &WaitTimeSyncTask{},
}
m.Tasks = []task.Interface{
checkPrepared,
checkTimeSync,
}
}

View File

@@ -1033,3 +1033,37 @@ func (a *SaveMasterHostConfig) Execute(runtime connector.Runtime) error {
}
return os.WriteFile(filepath.Join(runtime.GetBaseDir(), common.MasterHostConfigFile), content, 0644)
}
type WaitTimeSyncTask struct {
common.KubeAction
}
func (t *WaitTimeSyncTask) Execute(runtime connector.Runtime) error {
if chronyc, err := util.GetCommand(common.CommandChronyc); err == nil && chronyc != "" {
ticker := time.NewTicker(2 * time.Second)
timeout := time.NewTimer(5 * time.Minute)
defer ticker.Stop()
defer timeout.Stop()
for {
select {
case <-ticker.C:
// output format:
// 68839BAF,104.131.155.175,3,1772592384.619310832,-0.001840593,0.001674238,0.001874871,-5.194,-0.001,0.112,0.162520304,0.010412607,1035.0,Normal
if res, err := runtime.GetRunner().Cmd(fmt.Sprintf("%s -c tracking", chronyc), false, true); err != nil {
logger.Errorf("failed to execute chronyc tracking: %v", err)
return err
} else {
resToken := strings.Split(res, ",")
// if the stratum of the server is 10 which means the local reference (hardware RTC) is active.
if strings.ToLower(resToken[2]) != "10" { // Stratum
logger.Infof("time synchronization is normal")
return nil
}
}
case <-timeout.C:
return fmt.Errorf("timeout waiting for time synchronization")
}
}
}
return nil
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/utils"
)
type WelcomeMessage struct {
@@ -73,7 +72,7 @@ func (t *WelcomeMessage) Execute(runtime connector.Runtime) error {
// If AMD GPU on Ubuntu 22.04/24.04, print warning about reboot for ROCm
if si := runtime.GetSystemInfo(); si.IsUbuntu() && (si.IsUbuntuVersionEqual(connector.Ubuntu2204) || si.IsUbuntuVersionEqual(connector.Ubuntu2404)) {
if hasAmd, _ := utils.HasAmdIGPU(runtime); hasAmd {
if hasAmd, _ := connector.HasAmdAPUOrGPU(runtime); hasAmd {
logger.Warnf("\x1b[31mWarning: To enable ROCm, please reboot your machine after activation.\x1b[0m")
fmt.Println()
}

View File

@@ -380,7 +380,7 @@ func (a *upgradeGPUDriverIfNeeded) Execute(runtime connector.Runtime) error {
if err != nil {
return errors.Wrap(errors.WithStack(err), "kubeclient create error")
}
err = gpu.UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), &targetDriverVersionStr, ptr.To(common.CurrentVerifiedCudaVersion), ptr.To("true"))
err = gpu.UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), &targetDriverVersionStr, ptr.To(common.CurrentVerifiedCudaVersion), ptr.To("true"), ptr.To(gpu.NvidiaCardType))
if err != nil {
return err
}

View File

@@ -1,67 +0,0 @@
package utils
import (
"fmt"
"os"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/beclab/Olares/cli/pkg/core/connector"
)
func HasAmdIGPU(execRuntime connector.Runtime) (bool, error) {
// Detect by CPU model names that bundle AMD AI NPU/graphics
targets := []string{
"AMD Ryzen AI Max+ 395",
"AMD Ryzen AI Max 390",
"AMD Ryzen AI Max 385",
"AMD Ryzen AI 9 HX 375",
"AMD Ryzen AI 9 HX 370",
"AMD Ryzen AI 9 365",
}
// try lscpu first: extract 'Model name' field
out, err := execRuntime.GetRunner().SudoCmd("lscpu 2>/dev/null | awk -F': *' '/^Model name/{print $2; exit}' || true", false, false)
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
// fallback to /proc/cpuinfo
out, err = execRuntime.GetRunner().SudoCmd("awk -F': *' '/^model name/{print $2; exit}' /proc/cpuinfo 2>/dev/null || true", false, false)
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
return false, nil
}
func RocmVersion() (*semver.Version, error) {
const rocmVersionFile = "/opt/rocm/.info/version"
data, err := os.ReadFile(rocmVersionFile)
if err != nil {
// no ROCm installed, nothing to check
if os.IsNotExist(err) {
return nil, err
}
return nil, err
}
curStr := strings.TrimSpace(string(data))
cur, err := semver.NewVersion(curStr)
if err != nil {
return nil, fmt.Errorf("invalid rocm version: %s", curStr)
}
return cur, nil
}

View File

@@ -1,7 +1,7 @@
project_name: olaresd
builds:
- env:
- CGO_ENABLED=0
- CGO_ENABLED=1
# - CC=aarch64-linux-gnu-gcc
# - CXX=aarch64-linux-gnu-g++
main: ./cmd/terminusd/main.go
@@ -17,6 +17,12 @@ builds:
goamd64: v1
env:
- CGO_ENABLED=1
- goarch: arm64
goos: linux
env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc
- CXX=aarch64-linux-gnu-g++
tags:
containers_image_openpgp
ldflags:

View File

@@ -20,6 +20,9 @@ build: fmt vet ;$(info $(M)...Begin to build terminusd.) @
build-linux: fmt vet ;$(info $(M)...Begin to build terminusd (linux version).) @
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o bin/olaresd cmd/terminusd/main.go
build-arm: fmt vet ;$(info $(M)...Begin to build terminusd (linux version).) @
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -o bin/olaresd cmd/terminusd/main.go
build-linux-in-docker:
docker run -it --platform linux/amd64 --rm \
-v $(current_dir):/olaresd \
@@ -27,3 +30,11 @@ build-linux-in-docker:
-e DEBIAN_FRONTEND=noninteractive \
golang:1.24.11 \
sh -c "apt-get -y update; apt-get -y install libudev-dev libpcap-dev; make build-linux"
build-arm-in-docker:
docker run -it --platform linux/arm64 --rm \
-v $(current_dir):/olaresd \
-w /olaresd \
-e DEBIAN_FRONTEND=noninteractive \
golang:1.24.11 \
sh -c "apt-get -y update; apt-get -y install libudev-dev libpcap-dev; make build-arm"

View File

@@ -31,7 +31,7 @@ require (
github.com/jaypipes/ghw v0.13.0
github.com/jochenvg/go-udev v0.0.0-20171110120927-d6b62d56d37b
github.com/joho/godotenv v1.5.1
github.com/klauspost/cpuid/v2 v2.2.8
github.com/klauspost/cpuid/v2 v2.3.0
github.com/labstack/echo/v4 v4.0.0-00010101000000-000000000000
github.com/libp2p/go-netroute v0.2.2
github.com/mackerelio/go-osstat v0.2.5

View File

@@ -205,8 +205,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -471,7 +471,6 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=

View File

@@ -1,5 +1,5 @@
//go:build !(linux && amd64)
// +build !linux !amd64
//go:build !linux
// +build !linux
package intranet

View File

@@ -1,5 +1,5 @@
//go:build linux && amd64
// +build linux,amd64
//go:build linux
// +build linux
package intranet

View File

@@ -27,7 +27,7 @@ func WithSerial(ctx context.Context, serial string) context.Context {
}
func (w *usbWatcher) Watch(ctx context.Context) {
retry := 1
retry := 3
devs, err := utils.DetectdUsbDevices(ctx)
for {
if err != nil {

View File

@@ -43,11 +43,12 @@ type state struct {
Disk string `json:"disk"`
// network info
WikiConnected bool `json:"wifiConnected"`
WifiSSID *string `json:"wifiSSID,omitempty"`
WiredConnected bool `json:"wiredConnected"`
HostIP string `json:"hostIp"`
ExternalIP string `json:"externalIp"`
WikiConnected bool `json:"wifiConnected"`
WifiSSID *string `json:"wifiSSID,omitempty"`
WiredConnected bool `json:"wiredConnected"`
HostIP string `json:"hostIp"`
ExternalIP string `json:"externalIp"`
ExternalIPProbeTime time.Time `json:"-"`
// installing / uninstalling / upgrading state
InstallingState ProcessingState `json:"installingState"`
@@ -130,7 +131,8 @@ func CheckCurrentStatus(ctx context.Context) error {
klog.Info("current state: ", CurrentState.TerminusState)
}()
utils.ForceMountHdd(ctx)
// Deprecated, only for Olares Zero
// utils.ForceMountHdd(ctx)
// set default value
if CurrentState.TerminusVersion == nil {
@@ -255,7 +257,10 @@ func CheckCurrentStatus(ctx context.Context) error {
}
CurrentState.HostIP = hostIp
CurrentState.ExternalIP = nets.GetMyExternalIPAddr()
if time.Since(CurrentState.ExternalIPProbeTime) > 1*time.Minute {
CurrentState.ExternalIP = nets.GetMyExternalIPAddr()
CurrentState.ExternalIPProbeTime = time.Now()
}
// get olares state

View File

@@ -181,6 +181,7 @@ var (
// {"installing k8s and kubesphere", "3%", 3},
// {"Generating \"ca\" certificate and key", "3%", 3},
// {"PatchKsCoreStatus success", "6%", 6},
{"time synchronization is normal", "3%", 3},
{"k8s and kubesphere installation is complete", "10%", 10},
{"Installing account ...", "15%", 15},
{"Installing settings ...", "20%", 20},

View File

@@ -4,14 +4,17 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"io/ioutil"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"strings"
"time"
"github.com/beclab/Olares/daemon/pkg/commands"
"github.com/gofiber/fiber/v2/log"
"github.com/libp2p/go-netroute"
pkg_errors "github.com/pkg/errors"
"github.com/txn2/txeh"
@@ -267,15 +270,7 @@ func GetHostIpFromHostsFile(domain string) (string, error) {
return ip, nil
}
// GetMyExternalIPAddr get my network outgoing ip address
func GetMyExternalIPAddr() string {
sites := map[string]string{
"httpbin": "https://httpbin.org/ip",
"ifconfigme": "https://ifconfig.me/all.json",
"externalip": "https://myexternalip.com/json",
"joinolares": "https://myip.joinolares.cn/ip",
}
type httpBin struct {
Origin string `json:"origin"`
}
@@ -295,80 +290,80 @@ func GetMyExternalIPAddr() string {
IP string `json:"ip"`
}
var unmarshalFuncs = map[string]func(v []byte) string{
"httpbin": func(v []byte) string {
var hb httpBin
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
return hb.Origin
}
return ""
},
"ifconfigme": func(v []byte) string {
var ifMe ifconfigMe
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
return ifMe.IPAddr
}
return ""
},
"externalip": func(v []byte) string {
var extip externalIP
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
return extip.IP
}
return ""
},
"joinolares": func(v []byte) string {
return strings.TrimSpace(string(v))
},
type siteConfig struct {
url string
unmarshalFunc func(v []byte) string
}
ch := make(chan any, len(sites))
for site := range sites {
go func(name string) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
c := http.Client{Timeout: 5 * time.Second}
resp, err := c.Get(sites[name])
if err != nil {
ch <- err
return
}
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
ch <- err
return
}
ip := unmarshalFuncs[name](respBytes)
//println(name, site, ip)
ch <- ip
}(site)
externalIPServiceURL, err := url.JoinPath(commands.OLARES_REMOTE_SERVICE, "/myip/ip")
if err != nil {
klog.Error("failed to parse external IP service URL, ", err)
return ""
}
tr := time.NewTimer(time.Duration(5*len(sites)+3) * time.Second)
LOOP:
for i := 0; i < len(sites); i++ {
select {
case r, ok := <-ch:
if !ok {
continue
}
switch v := r.(type) {
case string:
ip := net.ParseIP(v)
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
return v
sites := []siteConfig{
{
url: externalIPServiceURL,
unmarshalFunc: func(v []byte) string {
return strings.TrimSpace(string(v))
},
},
{
url: "https://httpbin.org/ip",
unmarshalFunc: func(v []byte) string {
var hb httpBin
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
return hb.Origin
}
case error:
klog.Warningf("got an error, %v", v)
}
case <-tr.C:
tr.Stop()
klog.Warning("timed out")
break LOOP
return ""
},
},
{
url: "https://ifconfig.me/all.json",
unmarshalFunc: func(v []byte) string {
var ifMe ifconfigMe
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
return ifMe.IPAddr
}
return ""
},
},
{
url: "https://myexternalip.com/json",
unmarshalFunc: func(v []byte) string {
var extip externalIP
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
return extip.IP
}
return ""
},
},
}
client := http.Client{
Timeout: 3 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
for _, site := range sites {
resp, err := client.Get(site.url)
if err != nil {
log.Warnf("failed to get external ip from %s, %v", site.url, err)
continue
}
respBytes, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
log.Warnf("failed to read response from %s, %v", site.url, readErr)
continue
}
ipStr := site.unmarshalFunc(respBytes)
ip := net.ParseIP(ipStr)
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
return ipStr
}
}

View File

@@ -5,6 +5,7 @@ package utils
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
@@ -42,7 +43,7 @@ func detectdStorageDevices(ctx context.Context, bus string) (usbDevs []storageDe
for _, d := range ds {
if d.Properties()["ID_BUS"] == bus {
usbs = append(usbs, d)
} else if d.Properties()["ID_BUS"] == "ata" &&
} else if (d.Properties()["ID_BUS"] == "ata" || d.Properties()["ID_BUS"] == "scsi") &&
d.Properties()["ID_USB_TYPE"] == "disk" &&
bus == "usb" {
usbs = append(usbs, d)
@@ -97,14 +98,18 @@ func detectdStorageDevices(ctx context.Context, bus string) (usbDevs []storageDe
idSerial := device.Properties()["ID_SERIAL"]
idSerialShort := device.Properties()["ID_SERIAL_SHORT"]
idUsbSerial := device.Properties()["ID_USB_SERIAL"]
idUsbSerialShort := device.Properties()["ID_USB_SERIAL_SHORT"]
partUUID := device.Properties()["ID_PART_ENTRY_UUID"]
usbDevs = append(usbDevs, storageDevice{
DevPath: devPath,
Vender: vender,
IDSerial: idSerial,
IDSerialShort: idSerialShort,
PartitionUUID: partUUID,
DevPath: devPath,
Vender: vender,
IDSerial: idSerial,
IDSerialShort: idSerialShort,
IDUsbSerial: idUsbSerial,
IDUsbSerialShort: idUsbSerialShort,
PartitionUUID: partUUID,
})
}
@@ -199,7 +204,10 @@ func MountedHddPath(ctx context.Context) ([]string, error) {
func FilterBySerial(serial string) func(dev storageDevice) bool {
return func(dev storageDevice) bool {
return strings.HasSuffix(serial, dev.IDSerial) || strings.HasSuffix(serial, dev.IDSerialShort)
return strings.HasSuffix(serial, dev.IDSerial) ||
strings.HasSuffix(serial, dev.IDSerialShort) ||
strings.HasSuffix(serial, dev.IDUsbSerial) ||
strings.HasSuffix(serial, dev.IDUsbSerialShort)
}
}
@@ -270,7 +278,17 @@ func MountUsbDevice(ctx context.Context, mountBaseDir string, dev []storageDevic
continue
}
if err = mounter.Mount(d.DevPath, mkMountDir, "", []string{"uid=1000", "gid=1000"}); err != nil {
options := []string{}
fsType, err := getFsTypeOfDevice(ctx, d.DevPath)
if err != nil {
klog.Warning("get fs type of device error, ", err, ", ", d.DevPath)
} else {
if strings.Contains(fsType, "FAT") || strings.Contains(fsType, "NTFS") {
options = append(options, "uid=1000", "gid=1000")
}
}
if err = mounter.Mount(d.DevPath, mkMountDir, "", options); err != nil {
klog.Warning("mount usb error, ", err, ", ", d.DevPath, ", ", mkMountDir)
// clear the empty mount dir
// do not use remove all, only remove the mount point path, assume it's an empty dir
@@ -685,3 +703,35 @@ func isDeviceExists(devicePath string) bool {
_, err := os.Stat(devicePath)
return !os.IsNotExist(err)
}
func getFsTypeOfDevice(ctx context.Context, devicePath string) (string, error) {
// output format
// {
// "blockdevices": [
// {
// "fstype": "ext4"
// }
// ]
// }
cmd := exec.CommandContext(ctx, "lsblk", "-f", devicePath, "-o", "fstype", "-J")
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
var result struct {
BlockDevices []struct {
FsType string `json:"fstype"`
} `json:"blockdevices"`
}
if err := json.Unmarshal(output, &result); err != nil {
return "", err
}
if len(result.BlockDevices) == 0 {
return "", fmt.Errorf("no block devices found for %s", devicePath)
}
return result.BlockDevices[0].FsType, nil
}

View File

@@ -3,11 +3,13 @@ package utils
import "strings"
type storageDevice struct {
DevPath string
Vender string
IDSerial string
IDSerialShort string
PartitionUUID string
DevPath string
Vender string
IDSerial string
IDSerialShort string
IDUsbSerial string
IDUsbSerialShort string
PartitionUUID string
}
type mountedPath struct {

View File

@@ -24,9 +24,13 @@ export const oneSidebar: DefaultTheme.Sidebar = {
link: "/one/first-boot",
},
{
text: "Access Olares securely",
text: "Access Olares via VPN",
link: "/one/access-olares-via-vpn",
},
{
text: "Access Olares via .local domain",
link: "/one/access-olares-via-local-domain",
},
{
text: "Redeem membership",
link: "/one/redeem-membership",
@@ -202,10 +206,6 @@ export const oneSidebar: DefaultTheme.Sidebar = {
}
]
},
{
text: "Create a bootable USB",
link: "/one/create-drive",
},
]
},
{
@@ -216,14 +216,28 @@ export const oneSidebar: DefaultTheme.Sidebar = {
link: "/one/update",
},
{
text: "Back up & restore",
text: "Back up & restore data",
link: "/one/backup-resotre",
},
{
text: "Factory reset",
link: "/one/factory-reset",
text: "Restore Olares One",
collapsed: true,
items: [
{
text: "Factory reset",
link: "/one/factory-reset",
},
{
text: "Restore BIOS defaults",
link: "/one/factory-reset-in-bios",
},
{
text: "Reinstall Olares OS",
link: "/one/create-drive",
},
],
},
]
},
],
}
}

View File

@@ -24,11 +24,15 @@ export const oneSidebar: DefaultTheme.Sidebar = {
link: "/zh/one/first-boot",
},
{
text: "Access Olares securely",
text: "Access Olares via VPN",
link: "/zh/one/access-olares-via-vpn",
},
{
text: "Redeem Olares Space membership",
text: "Access Olares via .local domain",
link: "/zh/one/access-olares-via-local-domain",
},
{
text: "Redeem membership",
link: "/zh/one/redeem-membership",
},
]
@@ -165,7 +169,7 @@ export const oneSidebar: DefaultTheme.Sidebar = {
},
{
text: "Connect two Olares One",
link: "/zh/one/connect-two-olares-one"
link: "/zh/one/connect-two-olares-one",
// items:
// [
// {
@@ -202,10 +206,6 @@ export const oneSidebar: DefaultTheme.Sidebar = {
}
]
},
{
text: "Create a bootable USB",
link: "/zh/one/create-drive",
},
]
},
{
@@ -216,14 +216,28 @@ export const oneSidebar: DefaultTheme.Sidebar = {
link: "/zh/one/update",
},
{
text: "Back up & restore",
text: "Back up & restore data",
link: "/zh/one/backup-resotre",
},
{
text: "Factory reset",
link: "/zh/one/factory-reset",
text: "Restore Olares One",
collapsed: true,
items: [
{
text: "Factory reset",
link: "/zh/one/factory-reset",
},
{
text: "Restore BIOS defaults",
link: "/zh/one/factory-reset-in-bios",
},
{
text: "Reinstall Olares OS",
link: "/zh/one/create-drive",
},
],
},
]
},
],
}
}

View File

@@ -3,6 +3,7 @@ outline: [2,3]
description: Learn the different methods to access Olares services locally for improved speed and offline capability.
---
# Access Olares services locally
Olares is designed to provide seamless access to your self-hosted services anytime, anywhere.
However, accessing your devices locally provides several advantages:
@@ -11,7 +12,6 @@ However, accessing your devices locally provides several advantages:
- **Offline independence**: Access your data and apps even when your internet service is unavailable.
## Objectives
By the end of this tutorial, you will learn how to:
- Establish a secure, high-speed local connection using the LarePass VPN.
@@ -34,74 +34,48 @@ There are four ways to establish a local connection:
## Method 1: Enable LarePass VPN
The LarePass VPN is designed to secure your connection while optimizing performance. When enabled, LarePass detects if you are on the same network as your device and switches to **Intranet** mode.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
<!--@include: ../../reusables/larepass-vpn.md{19,24}-->
Enable the LarePass VPN directly on the device you are currently using to access Olares.
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app, and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app, and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
Once enabled, check the status indicator in LarePass to verify the connection type:
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
<!--@include: ../../reusables/larepass-vpn.md{26,50}-->
## Method 2: Use `.local` domain
If you prefer not to install additional apps, you can access services using the `.local` domain. There are two domain formats available depending on your operating system.
:::info Use HTTP protocol
The `.local` domain does not support HTTPS. You must explicitly use `http://` at the beginning of the URL.
:::
If you prefer not to install additional apps, you can access services using the `.local` domain. There are two domain formats available depending on your operating system.
### Single-level domain (All operating systems)
:::warning Supported for community apps only
Olares system apps such as Desktop and Files do not support this URL format and will not load correctly.
:::
This format uses a single-level domain by connecting the entrance ID and the username with hyphens (`-`).
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### Multi-level domain (macOS and iOS only)
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS. This allows a local URL format that mirrors the remote address.
**Standard URL**
```plain
https://<entrance_id>.<username>.olares.com
```
**Local URL**
```plain
http://<entrance_id>-<username>-olares.local
```
### Multi-level domain
The multi-level format below matches the structure of your standard Olares URL. Use it as shown.
<!--@include: ../../reusables/local-domain.md{7,23}-->
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![Multi-level local domain](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
#### macOS and iOS
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS.
Therefore, no extra setup is needed. You can directly use local URL in your browser.
#### Windows
<!--@include: ../../reusables/local-domain.md{26,40}-->
## Method 3: Configure local DNS
For a seamless experience where standard URLs resolve to your local IP address automatically, you can configure your network DNS. This configuration ensures consistent access across all devices on the network without requiring individual client setup.
@@ -212,39 +186,7 @@ If the IP address starts with `192.168`, it indicates successful configuration.
## FAQs
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open **System Settings**, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
<!--@include: ../../reusables/larepass-vpn.md{50,75}-->
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.
### Why the `.local` domain does not work in Chrome (macOS)?
Chrome may fail to access local URLs if macOS blocks local network permissions.
To enable access:
1. Open Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find Google Chrome and Google Chrome Helper in the list and enable the toggles.
![Enable local network](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. Restart Chrome and try accessing the local URL again.
### Why does the application fail to load in an iFrame when using a `.local` domain on Chrome (macOS)?
Chrome might default to HTTPS when using local domains, and you might see a "connection not secure" warning.
![Incorrect local address](/images/manual/get-started/incorrect-local-address.png#bordered)
To address this, explicitly add the HTTP protocol (`http://`) to the beginning of the URL. This tells Chrome it's a local, non-encrypted connection, which is expected on your home network.
<!--@include: ../../reusables/local-domain.md{42,75}-->

View File

@@ -1,81 +1,48 @@
---
outline: [2,3]
description: Learn how to access your Olares services securely using the LarePass VPN.
description: Learn how to access your Olares services securely using LarePass VPN or the .local domain.
---
# Access Olares services securely using LarePass VPN
# Access Olares services securely
Typically, you access Olares services through a browser using a URL like `https://desktop.<username>.olares.com`. This way, you can reach your services from any device at any time.
Typically, you access Olares services through a browser using a URL like `https://desktop.<username>.olares.com`. This way, you can reach your services from any device at any time. You can access Olares securely from your home network or from elsewhere.
While this address works from anywhere, it's recommended to enable the LarePass VPN to ensure your connection is always secure and efficient. The client automatically detects your network environment and selects the best connection method:
- **At home**: It establishes a direct **Intranet** connection to allow faster file transfers on your local network.
- **From remote**: It switches to a secure encrypted tunnel to ensure you remain connected safely when accessing remotely.
- [Using LarePass VPN](#using-larepass-vpn): Use this whether you are on your home network or away.
- [Using the .local domain](#using-the-local-domain): Use this only when your client device and Olares are on the same LAN.
## Download LarePass
To use the secure VPN connection, the LarePass client must be installed on the device you are using.
- **Mobile**: Use the LarePass app installed during the Olares ID creation process.
- **Desktop**: Download and install the LarePass desktop client.
## Using LarePass VPN
1. Visit <AppLinkGlobal />.
2. Download the version compatible with your operating system.
It is recommended to enable the LarePass VPN to ensure your connection is always secure and efficient. The client automatically detects your network environment and selects the best connection method:
## Enable LarePass VPN
Once installed, enable the VPN directly on the device you are using to access Olares.
- **At home**: It establishes a direct Intranet connection to allow faster file transfers on your local network.
- **From remote**: It switches to a secure encrypted tunnel so you remain connected safely when accessing remotely.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
<!--@include: ../../reusables/larepass-vpn.md{19,24}-->
<tabs>
<template #On-LarePass-mobile-client>
Enable the LarePass VPN directly on the device you are currently using to access Olares.
1. Open the LarePass app and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
<!--@include: ../../reusables/larepass-vpn.md{26,50}-->
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
## Using the .local domain
1. Open the LarePass app and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
Use the `.local` domain when your device and Olares are on the same LAN.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
### URL format
## Verify the connection type
Once enabled, check the status indicator in LarePass to verify the connection type:
<!--@include: ../../reusables/local-domain.md{7,23}-->
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
### macOS
## FAQs
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open **System Settings**, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
No setup is needed. Use the local URL in your browser (for example, `http://desktop.<username>.olares.local`).
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
### Windows
<!--@include: ../../reusables/local-domain.md{26,40}-->
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
### FAQs
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.
<!--@include: ../../reusables/larepass-vpn.md{50,75}-->
<!--@include: ../../reusables/local-domain.md{42,75}-->
## Learn more
- [Access Olares locally](../best-practices/local-access.md): Explore detailed instructions for all available local network connection methods.
- [Network](../../developer/concepts/network.md): Learn about the different entry points in Olares.
- [Network](../../developer/concepts/network.md): Learn about the different entry points in Olares.

View File

@@ -65,13 +65,38 @@ Upon completion, you will receive an Organization Olares ID.
</template>
</Tabs>
## Import an existing account
## Import an account
You can also set up an account by importing an existing Olares ID:
You can import an existing Olares ID to LarePass using its 12-word mnemonic phrase to access your Olares services on a new device or another LarePass client.
:::tip Back up mnemonic phrase
You must have already [backed up the mnemonic phrase](back-up-mnemonics.md) for the Olares ID to import.
Make sure you have already [backed up the mnemonic phrase](back-up-mnemonics.md) for the Olares ID to import.
:::
1. In LarePass app, tap **Import an account**.
2. Enter the 12-word mnemonic phrase to import your Olares ID.
<Tabs>
<template #iOS-&-Android>
1. Open the LarePass app.
2. Tap your profile avatar.
3. On the Switch account page, tap **Add a new account** at the bottom.
4. Tap **Import an account**.
5. Enter the 12-word mnemonic phrase for your Olares ID.
</template>
<template #macOS-&-Windows>
1. Open the LarePass desktop client.
2. Click your profile avatar.
3. Click **Switch account**.
4. Click **Add a new account** at the bottom.
5. Enter the 12-word mnemonic phrase for your Olares ID.
</template>
<template #Chrome-extension>
1. Open the LarePass extension in Chrome.
2. Click the options icon above your profile avatar.
3. Click **Add a new account**.
4. Enter the 12-word mnemonic phrase for your Olares ID.
</template>
</Tabs>

View File

@@ -9,37 +9,46 @@ LarePass is the official cross-platform client software for Olares. It acts as a
![LarePass](/images/manual/larepass/larepass.png)
## Key features
- Account and identity management
- Secure file access and sync
- Device and network management
- Password and secret management
- Knowledge collection
### Account & identity management
Create and manage your Olares ID, connect integrations with other services, and back up your credentials securely.
- [Create an Olares ID](create-account.md)
- [Back up mnemonics](back-up-mnemonics.md)
- [Set or reset local password](back-up-mnemonics.md#set-up-local-password)
- [Manage integrations](integrations.md)
## Download LarePass
### Secure file access & sync
- [Manage files with LarePass](manage-files.md)
### iOS
Visit the [App Store product page](https://apps.apple.com/us/app/larepass/id6448082605) to download LarePass.
### Device & network management
Activate and manage Olares devices, and securely connect to Olares via LarePass VPN.
- [Activate your Olares device](activate-olares.md)
- [Upgrade Olares](manage-olares.md#upgrade-olares)
- [Log in to Olares with 2FA](activate-olares.md#two-factor-verification-with-larepass)
- [Manage Olares](manage-olares.md)
- [Switch networks](manage-olares.md#switch-from-wired-to-wireless-network)
- [Enable VPN for remote access](private-network.md)
### Android
Visit the [Google Play product page](https://play.google.com/store/apps/details?id=com.terminus.termipass), or download the latest APK directly from the [LarePass website](https://www.olares.com/larepass).
### Password & secret management
Use Vault to autofill credentials, store passwords, and generate 2FA codes across devices.
- [Autofill passwords](/manual/larepass/autofill.md)
- [Generate 2FA codes](/manual/larepass/two-factor-verification.md)
### macOS & Windows
Download the latest desktop client from the [LarePass website](https://www.olares.com/larepass).
### Knowledge collection
Use LarePass to collect web content and follow RSS feeds.
- [Collect content via LarePass extension](manage-knowledge.md#collect-content-via-the-larepass-extension)
- [Subscribe to RSS feeds](manage-knowledge.md#subscribe-to-rss-feeds)
### Chrome extension
The LarePass extension allows you to collect content and manage passwords directly from your browser. It currently supports Google Chrome only and must be installed manually.
:::warning Keep the extension folder
Your browser loads the extension from the folder you select. If you delete, move, or rename that folder, the extension will stop working.
Extract the ZIP file to a permanent location, such as a folder under your user directory, rather than a temporary directory.
:::
1. Visit the [LarePass website](https://www.olares.com/larepass) and download the extension ZIP file.
2. Extract the ZIP file to a permanent folder on your computer.
3. In Chrome, go to `chrome://extensions/`.
4. Enable **Developer mode** in the top-right corner.
5. Click **Load unpacked** and select the extracted extension folder.
:::tip Quick access
After installation, click the puzzle icon in your browser toolbar and pin the LarePass extension for one-click access.
:::
## Set up account
- On mobile devices, you can [create an Olares ID](/manual/larepass/create-account.md#create-an-olares-id) directly in the app.
- On the desktop client or Chrome extension, you must [import an Olares account](/manual/larepass/create-account.md#import-an-account).
## Feature comparison
@@ -180,39 +189,3 @@ Use LarePass to collect web content and follow RSS feeds.
</tr>
</tbody>
</table>
## Download and install LarePass
Get the latest version for your device at the [LarePass website](https://www.olares.com/larepass).
### Install the LarePass browser extension
<tabs>
<template #Install-from-Chrome-Web-Store>
1. Search for **LarePass** in the [Chrome Web Store](https://chrome.google.com/webstore).
2. Open the details page and click **Add to Chrome**.
3. Log into the LarePass extension by importing your Olares ID:
- Open the LarePass extension, and click **Import an account**.
- Enter the mnemonics for your Olares ID.
- Enter your Olares password to complete login.
</template>
<template #Install-offline>
1. Visit [https://www.olares.com/larepass](https://www.olares.com/larepass) and download the extension ZIP file.
2. Go to `chrome://extensions/` in your browser.
3. Enable **Developer mode** in the top-right corner.
4. Click **Load unpacked** and select the extracted LarePass extension folder.
5. Log in:
- Open the LarePass extension, and click **Import an account**.
- Enter the mnemonics for your Olares ID.
- Enter your Olares password to complete login.
</template>
</tabs>
:::tip Quick access
After installation, pin the LarePass extension from Chromes extension menu for one-click access.
:::

View File

@@ -1,53 +1,53 @@
# Get familiar with Desktop
The Desktop application serves as the primary interface for user interaction with the Olares system. It offers an intuitive and efficient way to manage and utilize both built-in system applications and those you install.
Desktop is the primary interface for interacting with Olares. From here, you can open and manage built-in system apps as well as the apps you install.
## Desktop concepts
## Desktop basics
![Desktp[]](/images/manual/olares/desktop.png)
![Desktop](/images/manual/olares/desktop.png#bordered)
### Dock & Launchpad
### Dock
* **Dock:** An application quick-launch bar located on the side of the screen.
* **Launchpad:** Accessed by clicking the "Launchpad" icon on the Dock, it displays all installed applications.
The Dock is an application quick-launch bar on the left side of the screen. Use it to open frequently used apps and access key Desktop features.
### Launchpad
Launchpad shows all installed applications. Click the Launchpad icon in the Dock to open it.
### Application windows
* Applications open in "window" mode by default.
* Windows support standard operations: dragging, resizing, minimizing, maximizing, and closing.
* **Search:** Enables quick application launching, file searching, and more.
By default, applications open in window mode as an embedded page within Desktop. You can manage windows like you would on a standard computer:
## Use the Launchpad
- Drag the title bar to move the window.
- Drag the window edges to resize it.
- Minimize, maximize, or close the window.
- Click <i class="material-symbols-outlined">open_in_new</i> to open the app in a new browser tab.
From the Launchpad, you can:
:::info
Some applications only support opening in a browser tab.
:::
* View all installed applications.
* Click an application icon to open it.
* Drag icons to reorder them within the Launchpad.
* Drag an icon to the Dock for quick access.
### Search and notifications
- **Search**: Quickly launch applications and find supported content across Olares.
- **Notifications**: Click the notification icon to view system and application notifications.
## Use Launchpad
From Launchpad, you can:
- View all installed applications.
- Click an application icon to open it.
- Drag icons to reorder them within Launchpad.
- Drag an icon to the Dock for quick access.
### Uninstall applications
1. Press and hold an application icon to enter the editing mode.
2. If an "X" appears in the top-right corner of the application icon, click it to delete the application.
1. Press and hold an application icon to enter editing mode.
2. If a <i class="material-symbols-outlined">close_small</i> icon appears in the top-left corner of the app icon, click it to uninstall the application.
::: tip Note
Built-in system applications such as Files, Market, and Profile cannot be uninstalled.
:::
### Control application windows
You can access applications via two modes.
By default, applications open in "window" mode, which is an iframe page embedded within the desktop. You can manipulate these windows much like standard computer windows:
* Drag the title bar to move the window.
* Drag the window's edges to resize it.
* Minimize, maximize, or close the window.
* Click the <i class="material-symbols-outlined">open_in_new</i> button to open the application in a new browser tab.
::: tip Note
Some applications only support opening in a tabbed view.
:::info
Built-in system applications such as Files, Market, and Settings cannot be uninstalled.
:::
## Search within Olares

View File

@@ -15,9 +15,10 @@ To unlock the full potential of Wise, it is recommended to install the following
- **Rss Subscribe**: Use it to subscribe to RSS feeds directly while browsing web pages.
- **YT-DLP**: Use it to download audio and video from supported web pages into Wise.
- **Twitter/X plugin**: Use it to save posts and download attached files from Twitter/X into Wise.
:::tip
Wise works without these apps, but in-browser subscription and media download will be unavailable until you install them.
Wise works without these apps, but some features require them, such as in-browser subscriptions, media downloads, and Twitter/X link recognition and saving.
:::
## Build your library
@@ -40,6 +41,10 @@ Import files directly from your computer, including PDFs, EPUBs, audio, video, a
1. Click <i class="material-symbols-outlined">add_circle</i> in the bottom-left menu bar, and select **Upload**.
2. Select one or more files from your local computer.
:::tip
You can also drag and drop files into the Wise interface.
:::
3. In the Upload files window, select the destination folder, then click **Confirm**.
#### Add items via link
@@ -52,11 +57,16 @@ If a link requires login or other access control, Wise may need cookies to fetch
1. Click <i class="material-symbols-outlined">add_circle</i> in the bottom-left menu bar, and select **Add Link**.
2. Paste or type a URL.
Wise analyzes the link and lists all actions available:
- **Save to library**: The content will be saved as an item in your library and added to **Inbox**.
Wise analyzes the link and lists all the available actions:
- **Save to library**: The content will be saved as an item in your library and added to **Inbox**. Twitter/X posts are supported when the Twitter/X plugin is installed.
- **Subscribe to RSS feed**: If Wise detects one or more RSS feeds for the site, they will be listed here. Select the feed you want to follow, and new items from that feed will be automatically [added to **Feeds**](./subscribe).
![Subscribe to RSS feed](/images/manual/olares/wise-add-link-subscribe.png#bordered){width=300}
- **Download file**: If Wise detects videos or other downloadable files on the page, this option will appear. Select the file you want to download to save it for offline access. **[YT-DLP](https://market.olares.com/app/market.olares/ytdlp)** is required.
- **Download file**: If Wise detects downloadable media (such as audio, video, or attached files in Twitter/X posts), this option will appear. Select the file you want to download to save it for offline access.
:::tip Install helper services
Some downloads require helper services:
- [YT-DLP](https://market.olares.com/app/market.olares/ytdlp) is commonly used to download audio or video from supported pages when downloadable media is available.
- [Twitter/X plugin](https://market.olares.com/app/market.olares/twitter) is required to download attached files from Twitter/X posts.
:::
![Download files](/images/manual/olares/wise-add-link-download.png#bordered){width=300}
Newly saved items will appear under their content type.
@@ -72,22 +82,23 @@ You can also save content to Wise directly from your browser using the [LarePass
Items saved via LarePass are added to your Wise library and appear in the main **Inbox** folder and under the appropriate content type.
### Monitor and manage media downloads
### Monitor and manage file tasks
When you add new audio or video content, Wise automatically creates download tasks and save media files to Olares. This:
Wise tracks background transfer tasks in two lists:
- Ensures your media is available offline.
- Protects your library if the original source is removed.
- Makes it faster to open and play items.
- **Download list**: Created when you add downloadable media. Wise downloads the files to Olares so you can access them offline.
- **Upload list**: Created when you upload local files into Wise. Wise tracks the upload progress and results.
To manage all download tasks:
To manage transfer tasks:
1. Go to **<i class="material-symbols-outlined">settings</i> Settings** > **Transmission** > **Download**.
2. Review the list of media downloads and their status.
3. You can:
- Click <i class="material-symbols-outlined">folder_open</i> to locate a downloaded file in Files.
- Click <i class="material-symbols-outlined">do_not_disturb_on</i> to remove it from the list.
1. Go to **<i class="material-symbols-outlined">settings</i> Settings** > **Download list** or **Upload list**.
2. Use the tabs to filter tasks:
- Download list tabs: **All**, **Downloading**, **Completed**, **Failed**
- Upload list tabs: **All**, **Uploading**, **Completed**, **Failed**.
3. Review the task list and status.
4. You can:
- Click <i class="material-symbols-outlined">folder_open</i> to locate the transferred file in Files.
- Click <i class="material-symbols-outlined">do_not_disturb_on</i> to remove it from the list.
## Use reading tools

View File

@@ -5,9 +5,9 @@ description: Manage cookies for Wise so it can access protected websites and fee
# Manage cookies for Wise
Some websites require a login to access their content. To allow Wise to fetch content from these protected sources, you may need to upload cookies so it can authenticate the connection.
Some websites require you to log in to access their content. To allow Wise to fetch content from these protected sources, you may need to upload cookies so Wise can authenticate its requests.
This grants Wise permission to pass login checks, ensuring that features like **Save to library** or **Subscribe to RSS feed** work securely on restricted sites.
This allows Wise to authenticate access, ensuring that features like **Save to library**, **Subscribe to RSS feed** and **Download file** work securely on restricted sites.
This guide explains how to manage cookies in Olares for Wise.

View File

@@ -0,0 +1,41 @@
---
outline: [2, 3]
description: Learn how to access your Olares services on the same network using the `.local` domain.
head:
- - meta
- name: keywords
content: Olares, .local domain, local access
---
# Access Olares via .local domain
When your computer or phone is on the same local network as Olares One, you can use a `.local` domain to reach your Olares services so traffic stays on your LAN.
## Prerequisites
**Hardware**
- Olares One is set up and connected to your network.
- A client device (computer or phone) on the same network as Olares One.
**LarePass** (Required for Windows)
- The LarePass desktop client is installed on your Windows device.
- You have imported your Olares ID on the LarePass desktop client.
## URL format
<!--@include: ../reusables/local-domain.md{10,23}-->
## macOS
No setup is needed. Use the local URL in your browser (for example, `http://desktop.<username>.olares.local`).
## Windows
<!--@include: ../reusables/local-domain.md{26,40}-->
## Troubleshooting
<!--@include: ../reusables/local-domain.md{42,75}-->
## Learn more
- [Access Olares services locally](../manual/best-practices/local-access.md): DNS configuration, hosts file, and other local access methods.

View File

@@ -21,75 +21,17 @@ While this address works from anywhere, it's recommended to enable the LarePass
- A client device (computer or mobile phone) with internet access.
## Step 1: Download LarePass
To use the secure VPN connection, the LarePass client must be installed on the device you are using to access Olares.
- **Mobile**: Use the LarePass app installed during the Olares ID creation process.
- **Desktop**: Download and install the LarePass desktop client.
1. Visit <AppLinkGlobal />.
2. Download the version compatible with your operating system.
3. Install the application and log in with your Olares ID.
<!--@include: ../reusables/larepass-vpn.md{7,16}-->
## Step 2: Enable LarePass VPN
Once installed, enable the VPN directly on the device.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
<!--@include: ../reusables/larepass-vpn.md{18,41}-->
## Step 3: Verify the connection type
Once enabled, check the status indicator in LarePass to confirm how you are connected:
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
<!--@include: ../reusables/larepass-vpn.md{42,49}-->
## Troubleshooting
### Why doesn't LarePass VPN work on my Mac anymore?
If the VPN was working previously but has stopped, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open System Settings, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Restart your Mac.
5. Open the LarePass desktop client and re-enable the VPN.
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software may mistakenly flag the LarePass client, blocking the VPN service.
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Check your antivirus software to see if LarePass was blocked.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and try enabling the VPN again.
## Learn more
- [Access Olares locally](../manual/best-practices/local-access.md): Explore detailed instructions for all available local network connection methods.
- [Access Olares terminal](access-terminal-ssh.md): Learn how to connect to the host shell using SSH or the Control Hub web terminal.
- [Network](../developer/concepts/network.md): Learn about the different entry points in Olares.
<!--@include: ../reusables/larepass-vpn.md{50,74}-->

View File

@@ -17,14 +17,6 @@ You can connect to the host shell using one of the following methods:
- **Control Hub Terminal** is a web-based interface for direct root access. It is recommended for quick tasks.
- **Secure Shell (SSH)** is the standard protocol for remote management and complex operations.
## Before you begin
- The default username and password for Olares One are both `olares`.
:::warning Reset default SSH password
Even if you primarily use the Control Hub terminal, you must reset this password immediately in **Settings** > **My hardware** to secure your device against unauthorized access.
:::
- SSH access grants powerful control over the system. Ensure you keep your credentials secure.
## Prerequisites
**Hardware**
- Your Olares One is set up and connected to a network.
@@ -33,9 +25,6 @@ You can connect to the host shell using one of the following methods:
**Experience**
- Basic familiarity with terminal commands and the command-line interface (CLI).
**LarePass** (Required for remote access)
- The LarePass app is installed on your device. This is required only if you plan to connect via SSH from a remote location outside your local network.
## Method 1: Access via Control Hub
For quick access without configuring SSH clients, you can use the web-based terminal built directly into Control Hub.
@@ -53,7 +42,7 @@ The Control Hub terminal runs as `root` by default. You do not need to use `sudo
SSH establishes a secure session over the network, allowing you to use command-line operations for Olares One on your current device.
### Get IP address of Olares One
### Step 1: Get IP address of Olares One
To connect via SSH, you first need to find the internal IP address of your Olares One.
1. Open the LarePass app, and go to **Settings** > **System** to navigate to the **Olares management** page.
@@ -67,43 +56,20 @@ You can check the IP using the `ifconfig` command in the Control Hub terminal.
Look for your active connection, typically named `enp3s0` (wired) or `wlo1` (wireless). The IP address follows `inet`.
:::
### Connect via SSH (local network)
If your computer and the Olares One are on the same Wi-Fi or LAN:
### Step 2: Check SSH password in Vault
<!--@include: ./reusables-reset-ssh.md{7,16}-->
### Step 3: Connect via SSH
1. Open a terminal on your computer.
2. Run the `ssh` command using Olares One's local IP address:
```bash
ssh <username>@<host_ip_address>
# The default username for Olares One is olares.
ssh olares@<host_ip_address>
```
For example:
3. Enter the password when prompted.
```bash
ssh olares@192.168.31.155
```
3. Enter the host password when prompted.
### Connect via SSH (remote access)
If you are away from home, you can use LarePass VPN to bridge the connection securely.
#### Allow SSH access via VPN
For security, SSH access via VPN is disabled by default. You must enable it once.
1. On Olares, open the Settings app.
2. Navigate to **VPN**.
3. Toggle on **Allow SSH via VPN**.
#### Enable LarePass VPN
1. Open the LarePass desktop client on your computer.
2. Click your avatar in the top-left corner and toggle on **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
3. Open a terminal on your computer.
4. Run the `ssh` command using Olares One's local IP address. LarePass handles the routing automatically.
```bash
ssh <username>@<host_ip_address>
```
For example:
```bash
ssh olares@192.168.31.155
```
5. Enter the host password when prompted.
## Reset SSH password
<!--@include: ./reusables-reset-ssh.md{19,}-->

View File

@@ -1,65 +1,84 @@
---
outline: [2, 3]
description: Learn how to create a bootable USB installer for Olares OS using balenaEtcher.
description: Reinstall Olares OS on Olares One using a bootable USB drive to restore the device to a clean initial state.
head:
- - meta
- name: keywords
content: Olares, Olares One, install Olares, bootable USB, ISO, balenaEtcher
content: Olares One, reinstall, Olares OS, bootable USB, installation USB
---
# Create a bootable USB drive <Badge type="tip" text="15 min"/>
# Reinstall Olares OS using bootable USB <Badge type="tip" text="15 min"/>
To reinstall or recover Olares OS on your Olares One, you must create a bootable USB installation drive. This drive allows you to reset the device to its factory state.
Reinstalling Olares OS returns your Olares One to a clean initial state. You can do this using the bootable USB drive included with Olares One.
:::warning Data loss warning
This process will erase all data on your USB drive. If you have important files on the drive, back them up before proceeding.
:::
:::warning Image compatibility
Olares One requires a specific system image designed for its hardware. If you install the generic self-hosted ISO, Olares One will fail to boot.
:::warning Data loss
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
:::
## Prerequisites
- USB flash drive: A drive with 8 GB or higher capacity.
:::info
Ensure that your USB drive is empty or backed up. The flashing process re-formats the drive, which means existing data on it will be permanently deleted.
:::
- Computer: A Windows, macOS, or Linux computer to perform the setup.
- Internet connection: Stable network for downloading the image file and related software.
**Hardware**<br>
- The bootable USB drive that came with Olares One.
:::tip Don't have the USB drive?
Download the [Olares One ISO](https://cdn.olares.com/one/v1.12.4-amd64.iso), which is device-specific and different from the standard Olares ISO, and flash it to a USB drive (8 GB or larger) using a tool such as [Balena Etcher](https://etcher.balena.io/).
:::
- A monitor and keyboard connected to Olares One.
## Step 1. Download Olares image
## Step 1: Boot from the USB drive
Click to download the [ISO image for Olares One](https://cdn.olares.com/one/olares.iso). The file `olares.iso` will be saved to your computer.
1. Insert the bootable USB drive into Olares One.
2. Power on Olares One or restart it if it is already running.
3. When the Olares logo appears, immediately press the **Delete** key repeatedly to enter **BIOS setup**.
![BIOS setup](/images/one/bios-setup.png#bordered)
## Step 2. Install flashing tool
4. Navigate to the **Boot** tab, set **Boot Option #1** to the USB drive, and then press **Enter**.
![Set boot option](/images/one/bios-set-boot-option.png#bordered)
It is recommended to use balenaEtcher for this task because it is free, easy to use, and works on all major operating systems.
5. Press **F10**, then select **Yes** to save and exit.
![Save and exit](/images/one/bios-save-usb-boot.png#bordered)
1. Go to the [balenaEtcher website](https://etcher.balena.io/).
2. Download and install the version appropriate for your computer (Windows, macOS, or Linux).
## Step 3. Flash the drive
Olares One will restart and boot into the Olares installer interface.
Use balenaEtcher to turn your USB stick into a bootable installer.
## Step 2: Install Olares to disk
1. Insert your USB flash drive into the computer.
2. Open balenaEtcher.
3. Follow the steps on the screen:
1. From the installer interface, select **Install Olares to Hard Disk** and press **Enter**.
![Olares installer](/images/one/olares-installer.png#bordered)
a. Click **Flash from file** and select the Olares ISO file you downloaded.
2. When prompted for the installation target, the installer shows a list of available disks. Type `/dev/` followed by the disk name (e.g. `nvme0n1`) from that list and press **Enter**.
![Select disk](/images/one/olares-installer-select-disk.png#bordered)
b. Click **Select target** and select your USB drive.
For example, to install to `nvme0n1`, enter:
```bash
/dev/nvme0n1
```
c. Click **Flash!** to start writing the installer to the USB drive.
3. When you see prompts about NVIDIA GPU drivers, press **Enter** to accept the default.
![Install NVIDIA drivers](/images/one/olares-installer-install-nvidia-drivers.png#bordered)
![Bootable USB](/images/one/balenaEtcher.png#bordered)
4. When you see the message below, the reinstallation is complete:
```bash
Installation completed successfully!
```
4. When the flashing process is completed, the USB drive is ready to use, and you can safely eject it.
5. Remove the USB drive, then press **Ctrl + Alt + Delete** to restart.
## Next steps
## Step 3: Verify the installation
You can now insert the bootable USB drive into Olares One or other target hardware to begin the installation.
After the reboot, the system starts in a clean factory state and shows a text-based Ubuntu login prompt.
## Resources
1. Log in with the default credentials:
- **Username**: `olares`
- **Password**: `olares`
![Log in](/images/one/olares-login.png#bordered)
- [Install Olares via ISO](../manual/get-started/install-linux-iso.md)
- [Install Olares](../manual/get-started/install-olares.md)
- [Installation FAQs](../manual/help/installation.md)
2. (Optional) Run the following command to verify the installation:
```bash
sudo olares-check
```
Example output:
![Olares check](/images/one/olares-check.png#bordered)
## Step 4: Complete activation via LarePass
You can then activate Olares One again via LarePass. For detailed instructions, see [First boot](first-boot.md).

View File

@@ -21,15 +21,16 @@ This dual-drive configuration physically isolates the systems. This ensures Olar
## Step 1: Boot into BIOS
1. Insert the Windows USB boot drive into a USB port on Olares One.
2. Power on Olares One or restart if it is already running.
3. Immediately press and hold the **Delete** key repeatedly until the BIOS setup screen appears.
2. Power on Olares One or restart it if it is already running.
3. When the Olares logo appears, immediately press the **Delete** key repeatedly to enter **BIOS setup**.
![BIOS setup](/images/one/bios-setup.png#bordered)
## Step 2: Boot from USB
1. Navigate to the **Boot** tab using the arrow keys on your keyboard.
2. Locate **Boot Option #1** or **Boot Override** and select your USB flash drive.
3. Press **Enter** to boot from the USB drive immediately.
4. The system will restart and load the Windows installation interface.
2. Set **Boot Option #1** to your Windows USB flash drive, and press **Enter**.
3. Press **F10**, then select **Yes** to save and exit BIOS.
4. The system restarts and boots from the USB drive into the Windows installation interface.
## Step 3: Install Windows
1. Follow the on-screen prompts to begin the Windows installation.
@@ -49,16 +50,16 @@ Because the operating systems are on separate physical drives, you switch betwee
### Switch to Olares OS
1. Restart Olares One.
2. Press **Delete** repeatedly to enter BIOS.
2. Press the **Delete** key repeatedly to enter **BIOS setup**.
3. Go to the **Boot** tab.
4. Set **Boot Option #1** to the SSD containing Olares OS.
5. Press **F4** to confirm.
5. Press **F10** to save and exit BIOS.
### Switch to Windows
1. Restart Olares One.
2. Press **Delete** repeatedly to enter BIOS.
2. Press the **Delete** key repeatedly to enter **BIOS setup**.
3. Set **Boot Option #1** to the secondary SSD containing Windows.
4. Press **F4** to confirm.
4. Press **F10** to save and exit BIOS.
## Resources
- [Install NVIDIA drivers on Windows](install-nvidia-driver.md)

View File

@@ -34,11 +34,13 @@ Partitioning a drive carries a risk of data loss. If you have important data on
## Step 1: Install Windows
1. Insert the Windows USB boot drive into a USB port on Olares One.
2. Power on Olares One or restart if it is already running.
3. Immediately press and hold the **Delete** key repeatedly until the BIOS setup screen appears.
2. Power on Olares One or restart it if it is already running.
3. When the Olares logo appears, immediately press the **Delete** key repeatedly to enter **BIOS setup**.
![BIOS setup](/images/one/bios-setup.png#bordered)
4. Navigate to the **Boot** tab using the arrow keys on your keyboard.
5. Locate **Boot Option #1** or **Boot Override** and select your USB flash drive.
6. Press **Enter** to boot from the USB drive immediately.
5. Set **Boot Option #1** to your Windows USB flash drive, and press **Enter**.
6. Press **F10**, then select **Yes** to save and exit BIOS. The system restarts and boots from the USB drive into the Windows installation interface.
7. Follow the on-screen prompts to begin the Windows installation.
8. When the installation finishes and the system restarts, unplug the Windows USB drive.
@@ -59,13 +61,14 @@ The system will boot into Windows automatically.
Olares OS runs on top of a Linux kernel. You will install Ubuntu to serve as the host system.
1. Insert the Ubuntu USB drive and restart Olares One.
2. Press **Delete** repeatedly to enter BIOS, then select the USB drive as the boot device.
3. Follow the installer prompts until you reach the **Installation type** screen.
4. Select **Install Ubuntu alongside Windows Boot Manager**.
2. When the Olares logo appears, press the **Delete** key repeatedly to enter **BIOS setup**.
3. Navigate to the **Boot** tab, set **Boot Option #1** to your Ubuntu USB flash drive, and then press **Enter**.
4. Press **F10**, then select **Yes** to save and exit BIOS. The system restarts and boots from the USB drive into the Ubuntu installer.
5. Follow the installer prompts until you reach the **Installation type** screen.
:::tip
If this option does not appear, select the manual installation option to manually assign the unallocated space to Ubuntu.
:::
5. When the installation finishes and the system restarts, unplug the Ubuntu USB drive.
6. When the installation finishes and the system restarts, unplug the Ubuntu USB drive.
The system will boot into Ubuntu automatically.
@@ -120,17 +123,17 @@ After setup is complete, the LarePass app returns to the home screen, and the br
You switch between Windows and Olares using the BIOS boot priority.
### Switch to Olares OS
1. Restart Olares One.
2. Press **Delete** repeatedly to enter BIOS.
2. Press the **Delete** key repeatedly to enter **BIOS setup**.
3. Go to the **Boot** tab.
4. Set **Boot Override** to Ubuntu.
5. Press **F4** to confirm.
5. Press **F10** to save and exit BIOS.
### Switch to Windows
1. Restart Olares One.
2. Press **Delete** repeatedly to enter BIOS.
2. Press the **Delete** key repeatedly to enter **BIOS setup**.
3. Go to the **Boot** tab.
4. Set **Boot Override** to Windows.
5. Press **F4** to confirm.
5. Press **F10** to save and exit BIOS.
## Troubleshooting

View File

@@ -0,0 +1,39 @@
---
outline: [2, 3]
description: Learn how to restore BIOS defaults on Olares One to return the device to its initial setup state.
head:
- - meta
- name: keywords
content: Olares One, BIOS defaults, restore, BIOS setup
---
# Restore BIOS defaults <Badge type="tip" text="10 min" />
Restoring BIOS defaults resets the firmware configuration and returns your Olares One to its initial setup state. If you have a monitor and keyboard connected, you can perform this directly in BIOS.
:::warning Data loss
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
:::
## Prerequisites
**Hardware**<br>
- A wired keyboard connected to your Olares One.
- A monitor connected to your Olares One.
## Step 1: Load optimized defaults in BIOS
1. Power on Olares One or restart it if it is already running.
2. When the Olares logo appears, immediately press the **Delete** key repeatedly to enter **BIOS setup**.
![BIOS setup](/images/one/bios-setup.png#bordered)
3. Press **F9**, then select **Yes** to restore factory settings.
![Load optimized defaults](/images/one/bios-load-optimized-defaults.png#bordered)
4. Press **F10**, then select **Yes** to save and exit. The device restarts automatically.
![Save and exit](/images/one/bios-save-load-defaults.png#bordered)
Once finished, Olares One reboots into the initial setup phase.
## Step 2: Complete activation via LarePass
You can then activate Olares One again via LarePass. For detailed instructions, see [First boot](first-boot.md).

View File

@@ -1,28 +1,22 @@
---
outline: [2, 3]
description: Learn how to restore your Olares One to factory settings using LarePass.
description: Learn how to factory reset your Olares One using LarePass.
head:
- - meta
- name: keywords
content: Factory reset, Olares One
content: factory reset, Olares One, LarePass
---
# Reset to factory settings <Badge type="tip" text="10 min" />
# Factory reset via LarePass <Badge type="tip" text="10 min" />
Resetting to factory settings returns your Olares One to the initial setup state.
If you have already activated Olares One and want to return it to the factory state, you can perform a reset in LarePass.
:::warning Data loss
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
:::
## Learning objectives
By the end of this tutorial, you will learn how to:
- Perform a factory reset from LarePass.
- Complete the required confirmation and local password verification.
## Prerequisites
Before starting, ensure that:
**Hardware**<br>
- Olares One is powered on.
- Your phone and Olares One are on the same network.
- You are signed in to LarePass as an administrator.
@@ -30,15 +24,17 @@ Before starting, ensure that:
## Reset Olares One to factory settings
1. Open LarePass on your phone and go to **Settings**.
2. In the **My Olares** card, tap **System** to enter the **Olares management** page.
2. In the **My Olares** card, tap **System** to open the **Olares management** page.
3. Tap **Restore to factory settings**.
4. Review the risk prompt, then tap **Restore to factory settings** to continue.
4. Review the risk prompt carefully, then tap **Restore to factory settings** to continue.
![Review risk prompt](/images/manual/larepass/review-risk-prompt.png#bordered)
5. Enter your local LarePass lock screen password and tap **Confirm**.
5. Enter your local LarePass lock-screen password and tap **Confirm**.
![Enter local unlock password](/images/manual/larepass/enter-password-to-uninstall.png#bordered)
If you have not set a local password, you will be prompted to set one first.
6. Wait for the reset process to complete.
Once finished, Olares One reboots into the initial setup phase. You will be redirected to the activation flow, where you can scan the local network to reinstall or reactivate Olares.
Once finished, Olares One reboots into the initial setup phase. You are redirected to the activation flow, where you can scan the local network to reactivate Olares.
For detailed instructions, see [First boot](first-boot.md).

View File

@@ -89,21 +89,17 @@ Your Olares ID is secured by a unique 12-word mnemonic phrase. This phrase is th
4. Enter the local password as prompted.
5. Write the 12 words onto the **Recovery Sheet**, and then store the sheet in a secure, offline location.
### Reset SSH password
<!--@include: ./reusables-reset-ssh.md{7,16}-->
For instructions on how to SSH into Olares One, see [Connect to Olares One via SSH](access-terminal-ssh.md).
### Access Olares services securely
For secure remote access without complex network configuration, it is recommended to enable the LarePass VPN.
See [Access Olares services securely using LarePass VPN](access-olares-via-vpn.md).
### Reset SSH password
If you plan to connect to your Olares One via terminal (SSH), you must update the default SSH password.
1. Open Olares Settings, on the **My hardware** page, click **Reset SSH password**.
2. In the dialog, enter a new SSH password that meets all strength requirements, then click **OK**.
3. Open the LarePass app and scan the QR code shown on the screen.
4. Click **Confirm** on LarePass to finish.
For details, see [Connect to Olares One via SSH](access-terminal-ssh.md).
### Explore
Olares OS comes with pre-installed system apps. You can also browse the **Market** to download additional applications that best suit your needs.

View File

@@ -0,0 +1,27 @@
---
search: false
---
## Reset SSH password
### Reset upon activation
Right after you activate Olares, you will be prompted to reset the SSH password on the LarePass app. The password is automatically generated and saved to your Vault.
To view the saved password in Vault:
1. Tap **Vault** in the LarePass app. When prompted, enter your local password to unlock.
2. In the top-left corner, tap **Authenticator** to open the side navigation, then tap **All vaults** to display all saved items.
![Switch Vault filter](/images/one/ssh-switch-filter.png#bordered)
3. Find the item with the <span class="material-symbols-outlined">terminal</span> icon and tap it to reveal the password.
![Check saved SSH password in Vault](/images/one/ssh-check-password-in-vault.png#bordered)
### Reset in Olares Settings
If you prefer to use an SSH password instead of the automatically generated one, you can manually reset the password in Settings.
1. Open Settings. On the **My Olares** page, select **My hardware**.
2. Select **Reset SSH login password** at the bottom.
![Reset SSH login password](/images/one/ssh-reset-password-in-settings.png#bordered){width=70%}
3. In the dialog, enter a new SSH password that meets all strength requirements, then click **OK**.
4. Open the LarePass app and scan the QR code shown on the screen.
5. Click **Confirm** on LarePass to finish.

View File

@@ -67,7 +67,12 @@ You need to access the Sunshine Web UI running on your Olares One to enter a pai
1. Copy the URL of your current Steam Headless browser tab.
2. Open a new browser tab and modify the URL to access port `47990`. The address varies depending on your network.
- **Same network**: Use HTTPS with your `.local` address. Either format works (dots or hyphens in the hostname):
- **Same network**: Use HTTPS with your `.local` address. Either
format works (dots or hyphens in the hostname):
:::info Sunshine and .local
Use HTTPS (not HTTP) with your `.local` address for this app.
:::
```plain
https://139ebc4f0.<your Olares ID>.olares.local:47990
@@ -135,9 +140,9 @@ The following steps demonstrate local streaming.
### Why can't I access the Sunshine Web UI using the `.local` address?
Olares supports `.local` addresses with the HTTP protocol for most services. The Sunshine Web UI is different because it requires HTTPS to secure local communication. If you use `http://` with your `.local` URL, the Sunshine page will not load.
For most Olares services, you use HTTP with `.local` addresses. Sunshine is an exception and requires HTTPS to secure local communication. If you use `http://` with your `.local` URL, the Sunshine page will not load.
To fix this, use `https://` instead of `http://` in your browser's address bar (for example, `https://139ebc4f0.<your Olares ID>.olares.local:47990`).
Use `https://` in your browser's address bar (for example, `https://139ebc4f0.<your Olares ID>.olares.local:47990`).
### Why isn't the game displaying in full screen?

View File

@@ -59,12 +59,12 @@ If you frequently save videos while browsing, the [LarePass extension](https://w
When you save a video to the library, Wise creates a record immediately, and the file download runs in the background.
1. In Wise, click <i class="material-symbols-outlined">settings</i> in the bottom-left menu bar, then select **Transmission**.
2. In the Download tab, check the list of downloads and their status.
3. You can:
- Click <i class="material-symbols-outlined">folder_open</i> to locate a downloaded file in Files.
- Click <i class="material-symbols-outlined">do_not_disturb_on</i> to remove it from the list.
1. In Wise, click <i class="material-symbols-outlined">settings</i> in the bottom-left menu bar, then select **Download list**.
2. Use the tabs to filter tasks: **All**, **Downloading**, **Completed**, **Failed**.
3. Review the task list and status.
4. You can:
- Click <i class="material-symbols-outlined">folder_open</i> to locate the transferred file in Files.
- Click <i class="material-symbols-outlined">do_not_disturb_on</i> to remove it from the list.
Once the download is complete, you can play the video directly inside Wise even without an internet connection.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 347 KiB

10
docs/reusables/README.md Normal file
View File

@@ -0,0 +1,10 @@
# Reusables
This directory holds shared content included in multiple docs via `<!--@include: path/to/reusables/file.md{start,end}-->`.
Add new reusable fragments here and document line ranges in a comment at the top of each file.
- **local-domain.md**: .local domain description, URL format, HTTP note, and troubleshooting (Chrome, Safari). Used by `manual/get-started/local-access.md`, `manual/best-practices/local-access.md`, and `one/access-olares-via-local-domain.md`.
- **larepass-vpn.md**: LarePass VPN procedure (Download, Enable, Verify connection type) and FAQs (Mac extension reset, Windows antivirus). Used by `manual/get-started/local-access.md`, `manual/best-practices/local-access.md`, and `one/access-olares-via-vpn.md`.

View File

@@ -0,0 +1,74 @@
---
search: false
---
<!-- Reusable LarePass VPN content. Include by line range.
Steps (no headings): Step 1 7-16, Step 2 18-41, Step 3 42-49.
FAQs: 50-75 -->
To use the secure VPN connection, the LarePass client must be installed on the device you are using to access Olares.
- **Mobile**: Use the LarePass app installed during the Olares ID creation process.
- **Desktop**: Download and install the LarePass desktop client.
1. Visit <AppLinkGlobal />.
2. Download the version compatible with your operating system.
3. Install the application and log in with your Olares ID.
Once installed, enable the VPN directly on the device.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
Once enabled, check the status indicator in LarePass to verify the connection type:
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open **System Settings**, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.

View File

@@ -0,0 +1,74 @@
---
search: false
---
<!-- Reusable .local domain content. Include by line range.
Order: description, URL+HTTP, Windows, then FAQs.
Ranges: description 7-8, URL+HTTP 10-23, Windows 25-40 (content only 26-40), FAQs 42-75 -->
When your device is on the same local network as Olares, you can use a `.local` domain to reach your services so traffic stays on your LAN.
Use a multi-level `.local` hostname that mirrors your standard URL. This format works with Olares system apps and community apps.
:::tip
Use `http://`, not `https://`, with the `.local` URL.
:::
**Standard URL**
```text
https://<entrance_id>.<username>.olares.com
```
**Local URL**
```text
http://<entrance_id>.<username>.olares.local
```
### Windows
On Windows, `.local` hostnames are not resolved by default. Use the LarePass desktop app to add the necessary entries to your hosts file so multi-level `.local` URLs resolve to your Olares device.
1. Open the LarePass app, click your avatar, then **Settings**.
2. Scroll to **Enable local service domain** and click **Add**. LarePass will update your hosts file automatically.
![Enable local service domain](/images/one/larepass-win-update-hosts.png#bordered)
3. When the update completes, a success message appears. If a command line window opens, you can close it.
4. (Optional) To verify the changes to the hosts file:
a. Go to `C:\Windows\System32\drivers\etc\`.
b. Open the `hosts` file in a text editor. You should see the `.local` entries that LarePass added.
![Hosts file updated by LarePass](/images/one/larepass-updated-hosts.png#bordered)
### Why doesn't the .local domain work in Chrome on macOS?
Chrome may block local URLs if macOS has not granted it local network access.
1. Open the Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find **Google Chrome** and **Google Chrome Helper** and turn their toggles on.
4. Restart Chrome and try the `.local` URL again.
![Enable local network](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
### Why does the app show "connection not secure" or fail to load in Chrome?
Chrome sometimes forces HTTPS for `.local` hostnames, which is not supported.
Use `http://` explicitly at the start of the URL (e.g. `http://desktop.<username>.olares.local`). On your home network, this unencrypted local connection is expected and keeps the `.local` domain working.
![Incorrect local address](/images/manual/get-started/incorrect-local-address.png#bordered)
### Why does the iframe flicker when I open a .local URL in Safari?
Safari applies stricter handling to `.local` (and other non-HTTPS) content in iframes, which can make the iframe flicker or reload. Enabling two options in **Privacy** settings fixes it.
To fix it:
1. Open **Safari** and go to **Settings**.
2. Open the **Privacy** tab.
3. Enable the two options:
- Prevent cross-site tracking
- Hide IP address from trackers
![Safari Privacy settings for .local](/images/manual/get-started/safari-privacy-settings.png#bordered){width=70%}
4. Reload the `.local` page.

View File

@@ -1,6 +1,6 @@
---
outline: [2, 3]
description: Learn how to install, configure, and integrate OpenClaw with Discord.
description: Learn how to install, configure, personalize, and integrate OpenClaw with Discord.
head:
- - meta
- name: keywords
@@ -15,18 +15,22 @@ It acts as an "always-on" operator that can execute real tasks, such as searchin
## Learning objectives
By the end of this tutorial, you are be able to:
- Install and initialize the OpenClaw environment.
- Pair and connect the OpenClaw CLI and the Control UI.
- Configure OpenClaw to use the local AI model Ollama.
- Personalize OpenClaw to establish its identity and behavior.
- Integrate OpenClaw with Discord.
- Enable the web search capability using Brave Search.
- Manage skills and plug-ins.
## Prerequisites
- Local model: Ensure Ollama is installed and running. You must have a tool-capable model installed, such as `glm-4.7-flash`, `qwen3`, and `llama3.1`. This tutorial uses `llama3.1:8b`.
- Local model: Ensure Ollama is installed and running. You must have a tool-capable model installed, such as `glm-4.7-flash`, `qwen3.5:27b`, and `gpt-oss:20b`. This tutorial uses `qwen3.5:27b`.
:::tip
OpenClaw requires a large "context window" (that is the AI's short-term memory) to handle complex tasks without forgetting your previous instructions. If you are using local models, it is recommended to select a model that natively supports a context window of at least 64K tokens.
:::
- Discord account: Required to create the bot application.
- Discord server: A server where you have permissions to add bots.
- (Optional) Brave search API key: Required for the agent to search the web for real-time information.
@@ -127,18 +131,18 @@ Run a quick setup for the agent in the OpenClaw CLI.
- **Default Session Key**: Enter `agent:main:main`.
7. Click **Connect**.
The connection error `disconnected[1008]:pairing required` occurs. This is expected and means the device connection is waiting for approval.
The connection error `pairing required` occurs. This is expected and means the device connection is waiting for approval.
8. Return to the OpenClaw CLI window and enter the following command:
```bash
openclaw devices approve --latest
```
9. When the terminal displays the approval message, return to the Control UI and refresh it.
9. When the terminal displays the approval message, return to the Control UI.
![Pair sucess](/images/manual/use-cases/new-pair-success.png#bordered)
Now the **STATUS** in the **Snapshot** panel should be **Connected**.
Now the **STATUS** in the **Snapshot** panel should be **OK**.
![Health OK](/images/manual/use-cases/openclaw-connected.png#bordered)
![Health OK](/images/manual/use-cases/openclaw-connected1.png#bordered)
:::tip For advanced users
If you prefer to fully customize your initial setup, you can run the `openclaw onboard` command instead to launch the interactive configuration wizard.
@@ -158,7 +162,7 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
- **Default Session Key**: Enter `agent:main:main`.
3. Click **Connect**.
The connection error `disconnected[1008]:pairing required` occurs. This is expected and means the device connection is waiting for approval.
The connection error `pairing required` occurs. This is expected and means the device connection is waiting for approval.
4. Return to the OpenClaw CLI window and enter the following command:
```bash
openclaw devices list
@@ -176,9 +180,9 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
```bash
openclaw devices approve {RequestID}
```
7. When the terminal displays the approval message, return to the Control UI. Now the **STATUS** in the **Snapshot** panel should be **Connected**.
7. When the terminal displays the approval message, return to the Control UI. Now the **STATUS** in the **Snapshot** panel should be **OK**.
![Health OK](/images/manual/use-cases/openclaw-connected.png#bordered)
![Health OK](/images/manual/use-cases/openclaw-connected1.png#bordered)
## Configure local AI model
@@ -190,7 +194,7 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
"agents": {
"defaults": {
"model": {
"primary": "ollama/llama3.1:8b"
"primary": "ollama/qwen3.5:27b"
},
"workspace": "/home/node/.openclaw/workspace",
"maxConcurrent": 4,
@@ -202,6 +206,67 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
```
4. Click **Save** in the upper-right corner. The system validates the config and restarts automatically to apply the changes.
## (Optional) Personalize OpenClaw
To make your OpenClaw bot more personalized, it is highly recommended to complete the persona setup process.
This process establishes the agent's identity, behavioral boundaries, and long-term memory through persona files. These files keep your agent's behavior consistent across all platforms and channels.
1. In the Control UI, select **Chat** from the left sidebar.
2. Ensure <i class="material-symbols-outlined">neurology</i> at the upper-right corner is enabled. This allows you to watch the agent think and edit persona files in real time.
3. Enter and send the following message to start:
```text
Wake up please!
```
The agent responds and starts interviewing you. You can establish rules, personality traits, and preferences. For example,
```text
- Call me Bella. I like simple language without technical jargons and
concise bulleted answers.
- You are John, a witty assistant who uses emojis.
- Never access my calendar without asking first, and never execute any
financial operations.
```
4. As you chat with the agent, look for the **Edit** messages. These indicate the agent is successfully writing your preferences to its core persona files, such as `IDENTITY.md`, `USER.md`, and `SOUL.md`.
![Persona files editing by OpenClaw](/images/manual/use-cases/openclaw-persona-recording.png#bordered){width=90%}
:::tip
If you do not see the intermediate persona file operations, refresh the page by clicking <i class="material-symbols-outlined">refresh</i> at the upper-right corner or by pressing F5.
:::
5. Continue the conversation until the agent gathers enough information. Then, it automatically deletes the temporary `BOOTSTRAP.md` file to finish the personalization process.
![Finish hatch agent](/images/manual/use-cases/openclaw-hatch-finish.png#bordered){width=90%}
6. (Optional) If the agent fails to update the persona files or delete `BOOTSTRAP.md`, explicitly instruct it to do so in the chat.
If the issue persists, resolve it using one of the following methods:
- **Increase the context window**: Select **Config** from the left sidebar, switch to the **Raw** tab, find the `models` section, and then increase the `contextWindow` value to at least 64K (200K is recommended).
:::tip
Note that a larger context window consumes more VRAM, so choose a value that your hardware can support.
:::
- **Change the model**: Switch to a model with better tool-calling and instructionfollowing capabilities.
7. Verify your agent's persona files are updated:
a. Open Files from the Launchpad.
b. Go to **Application** > **Data** > **clawdbot** > **config** > **workspace**.
c. Check the modified time of the `.md` files to identify which ones were recently updated, such as `USER.md` and `IDENTITY.md`.
![Persona files generated by OpenClaw](/images/manual/use-cases/openclaw-persona-files.png#bordered){width=90%}
d. (Optional) Double-click a file to verify that it contains your newly established rules such as name, language style, and restrictions.
:::tip Modify persona settings
To change these settings in the future, use one of the following methods:
- Ask the agent in the chat to update its rules.
- Download the `.md` files from this folder, edit them in a text editor, and re-upload them to overwrite the old ones.
:::
## Integrate with Discord
To chat with your agent remotely, connect it to a Discord bot.
@@ -340,7 +405,7 @@ OpenClaw officially recommends Brave Search. It uses an independent web index op
## Manage skills and plugins
OpenClaw can be extended using skills and plugins
OpenClaw can be extended using skills and plugins:
- Skills add new capabilities to the AI. For example, managing Model Context Protocol servers.
- Plugins extend the system to support additional channels or community features. For example, adding iMessage via BlueBubbles.

View File

@@ -34,74 +34,48 @@ Olares 的设计初衷是让你随时随地都能无缝访问自己的服务。
## 方法 1启用 LarePass VPN
LarePass VPN 旨在兼顾连接安全与性能优化。启用后LarePass 会自动检测设备是否处于同一网络,并切换至**内网**模式。
:::tip 始终启用 VPN 以进行远程访问
保持 LarePass VPN 启用。它会自动优先选择最快的可用路由,确保你无需手动切换即可获得最佳速度。
:::
:::info iOS 和 macOS 设置
在 iOS 或 macOS 上首次启用该功能时,系统可能会提示你添加 VPN 配置文件。允许此操作以完成设置。
:::
<!--@include: ../../reusables/larepass-vpn.md{19,24}-->
直接在你当前用于访问 Olares 的设备上启用 LarePass VPN。
在用来访问 Olares 的当前设备上直接启用 LarePass VPN。
<tabs>
<template #使用-LarePass-移动端>
1. 打开 LarePass 应用,进入**设置**。
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
![移动端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #使用-LarePass-桌面端>
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
2. 打开**专用网络连接**开关。
![桌面端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
启用后,可以查看 LarePass 中的网络状态以确认连接类型:
| 状态 | 描述 |
|:-----------|:---------------------------|
| **内网** | 通过本地局域网 IP 直连。速度最快。 |
| **P2P** | 设备间的直接加密隧道。连接速度快。 |
| **DERP** | 通过安全中继服务器路由,仅在无法直连时作为备用方案。 |
<!--@include: ../../reusables/larepass-vpn.md{26,50}-->
## 方法 2使用 `.local` 域名
若不希望安装额外应用,可使用 `.local` 域名访问服务。根据操作系统不同,有两种域名格式。
:::info 使用 HTTP 协议
`.local` 域名不支持 HTTPS。务必在 URL 开头显式添加 `http://`
:::
如果不希望安装额外应用,可使用 `.local` 域名访问服务。根据操作系统不同,有两种域名格式。
### 单级域名(所有操作系统适用)
:::warning 仅支持社区应用
Desktop 和文件管理器等 Olares 系统应用不支持此 URL 格式,因此无法正确加载。
:::
此格式通过连字符(`-`)连接入口 ID 和用户名来使用单级主机名。
- **默认 URL**
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**
```plain
http://<entrance_id>-<username>-olares.local
```
### 多级域名 (macOS 和 iOS 适用)
Apple 设备支持通过 [Bonjour](https://developer.apple.com/bonjour/)(零配置网络)进行本地服务发现,因此能够在 macOS 和 iOS 上解析 `.local` 下的多级域名。这使得本地 URL 格式可以与默认的远程访问地址保持结构一致。
**标准 URL**
```plain
https://<entrance_id>.<username>.olares.cn
```
**本地访问 URL**
```plain
http://<entrance_id>-<username>-olares.local
```
### 多级域名
The multi-level format below matches the structure of your standard Olares URL. Use it as shown.
<!--@include: ../../reusables/local-domain.md{7,23}-->
- **默认 URL**
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**
```plain
http://<entrance_id>.<username>.olares.local
```
![多级域名](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
#### macOS and iOS
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS.
Therefore, no extra setup is needed. You can directly use local URL in your browser.
#### Windows
<!--@include: ../../reusables/local-domain.md{26,40}-->
## 方法 3配置本地 DNS
为了获得无缝体验(即标准 URL 自动解析为你的本地 IP 地址),你可以配置网络 DNS。此配置确保网络上所有设备的访问一致无需单独设置客户端。
@@ -212,39 +186,7 @@ ping desktop.<username>.olares.cn
## 常见问题
### 为什么在 Mac 上无法再启用 LarePass VPN
如果之前成功启用过 VPN但现在停止工作可能需要重置系统扩展。
:::info
根据 macOS 版本不同,界面可能略有差异。
:::
1. 打开**系统设置**,搜索`扩展`,选择**登录项与扩展**。
2. 滚动到**网络扩展** 部分,点击信息图标 (ⓘ) 查看已加载的扩展。
3. 找到 LarePass点击三个点 (...),选择**删除扩展**。
4. 确认卸载。
5. 重启 Mac 并在 LarePass 桌面客户端中重新启用 VPN。
### 为什么我在 Windows 上无法启用 LarePass VPN
第三方杀毒软件可能会错误地将 LarePass 桌面客户端标记为可疑,从而阻止其启动 VPN 服务。
<!--@include: ../../reusables/larepass-vpn.md{50,75}-->
如果在首次打开 LarePass 时收到杀毒软件提示,请允许应用程序继续运行。
如果 VPN 仍然无法启用:
1. 打开安全软件,检查 LarePass 是否被拦截。
2. 将 LarePass 主程序添加到杀毒软件的白名单或排除项中。
3. 重启 LarePass 并启用 VPN。
### 为什么在 macOS 上Chrome 无法访问 `.local` 域名?
如果 macOS 未授予局域网访问权限Chrome 可能会无法访问本地 URL。
要启用访问权限:
1. 打开 Apple 菜单,进入**系统设置**。
2. 进入**隐私与安全性** > **局域网**。
3. 在列表中找到 Google Chrome 和 Google Chrome Helper并开启开关。
![启用局域网权限](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. 重启 Chrome 并再次尝试访问本地 URL。
### 为什么在 Chrome 上使用 `.local` 域名时,应用无法在 iFrame 中加载 (macOS)
使用本地域名时Chrome 可能会默认使用 HTTPS你可能会看到“连接不安全”的警告。
![本地地址错误](/images/manual/get-started/incorrect-local-address.png#bordered)
要解决此问题,在 URL 开头显式添加 HTTP 协议头 (`http://`),告诉浏览器这是一个仅在本地网络中使用的链接。
<!--@include: ../../reusables/local-domain.md{42,75}-->

View File

@@ -1,81 +1,48 @@
---
outline: [2,3]
description: 了解如何使用 LarePass VPN 安全访问 Olares 服务。
description: 了解如何使用 LarePass VPN 或 .local 域名安全访问 Olares 服务。
---
# 使用 LarePass VPN 安全访问 Olares 服务
# 安全访问 Olares 服务
通常,你会使用类似 `https://desktop.<username>.olares.cn` 的网址在浏览器中访问 Olares。这种方式允许你在任何设备上随时随地访问服务。
通常,你会通过浏览器使用类似 `https://desktop.<username>.olares.com` 的网址访问 Olares,从而在任何设备上随时访问服务。你可以从家庭网络或外网安全访问 Olares。
虽然该地址可从任意网络访问,但为了获得更安全、高效的连接,建议启用 LarePass VPN。客户端会自动检测网络环境并智能切换连接模式
- **居家**:建立直连内网通道,大幅提升局域网文件传输速度
- **远程**:切换至加密安全隧道,保障远程访问的数据安全。
- [使用 LarePass VPN](#使用-larepass-vpn):无论在内网还是外网均可使用。
- [使用 .local 域名](#使用-local-域名):仅当客户端设备与 Olares 在同一局域网时使用
## 下载 LarePass
要使用安全 VPN 连接,需先在当前设备上安装 LarePass 客户端。
- **移动端**:使用在创建 Olares ID 过程中安装的 LarePass 移动端。
- **桌面端**:下载并安装 LarePass 桌面客户端。
## 使用 LarePass VPN
1. 访问 <AppLinkCN />。
2. 下载对应操作系统的版本。
建议启用 LarePass VPN以获得更安全、高效的连接。客户端会自动检测网络环境并选择最佳连接方式
## 启用 LarePass VPN
安装完成后,在当前设备上启用 VPN
- **居家**:建立直连内网,加快局域网文件传输。
- **远程**:切换至加密隧道,保障远程访问安全
:::tip 始终启用 VPN 以进行远程访问
建议保持 LarePass VPN 开启。系统会自动优选最佳路由,让你无需手动切换即可确保持续获得最快速度。
:::
:::info iOS 和 macOS 设置
首次在 iOS 或 macOS 上开启此功能时,系统可能会弹窗请求添加 VPN 配置。允许此操作以完成设置。
:::
<!--@include: ../../reusables/larepass-vpn.md{19,24}-->
<tabs>
<template #使用-LarePass-移动端>
在用来访问 Olares 的当前设备上直接启用 LarePass VPN。
1. 打开 LarePass 应用,进入**设置**。
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
<!--@include: ../../reusables/larepass-vpn.md{26,50}-->
![移动端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #使用-LarePass-桌面端>
## 使用 .local 域名
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单
2. 打开**专用网络连接**开关。
当设备与 Olares 在同一局域网时,可使用 .local 域名
![桌面端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
### URL 格式
## 确认连接状态
开启后,查看 LarePass 中的状态标签来确认当前的连接模式:
<!--@include: ../../reusables/local-domain.md{7,23}-->
| 状态 | 描述 |
|:-----------|:---------------------------|
| **内网** | 通过本地局域网 IP 直连。速度最快。 |
| **P2P** | 设备间的直接加密隧道。连接速度快。 |
| **DERP** | 通过安全中继服务器路由,仅在无法直连时作为备用方案。 |
### macOS
## 常见问题
### 为什么在 Mac 上无法再启用 LarePass VPN
如果之前成功启用过 VPN但现在停止工作可能需要重置系统扩展。
:::info
根据 macOS 版本不同,界面可能略有差异。
:::
1. 打开**系统设置**,搜索`扩展`,选择**登录项与扩展**。
2. 滚动到**网络扩展** 部分,点击信息图标 (ⓘ) 查看已加载的扩展。
3. 找到 LarePass点击三个点 (...),选择**删除扩展**。
4. 确认卸载。
5. 重启 Mac 并在 LarePass 桌面客户端中重新启用 VPN。
无需配置。在浏览器中直接使用本地 URL例如 `http://desktop.<username>.olares.local`)。
### 为什么我在 Windows 上无法启用 LarePass VPN
第三方杀毒软件可能会错误地将 LarePass 桌面客户端标记为可疑,从而阻止其启动 VPN 服务。
### Windows
<!--@include: ../../reusables/local-domain.md{26,40}-->
如果在首次打开 LarePass 时收到杀毒软件提示,请允许应用程序继续运行。
### 常见问题
如果 VPN 仍然无法启用:
1. 打开安全软件,检查 LarePass 是否被拦截。
2. 将 LarePass 主程序添加到杀毒软件的白名单或排除项中。
3. 重启 LarePass 并启用 VPN。
<!--@include: ../../reusables/larepass-vpn.md{50,75}-->
<!--@include: ../../reusables/local-domain.md{42,75}-->
## 了解更多
- [本地访问 Olares 服务](../best-practices/local-access.md)了解所有 Olares 本地连接方式
- [网络](../../../zh/developer/concepts/network.md):了解 Olares 中的应用的各类入口。
- [本地访问 Olares 服务](../best-practices/local-access.md)查看所有本地网络连接方式的详细说明
- [网络](../../developer/concepts/network.md):了解 Olares 中的各类应用入口。

Some files were not shown because too many files have changed in this diff Show More