En estos días he estado terminando de programar Sublime POS 4 multinegocio con soporte para internet. Básicamente es:
- Sublime POS 4 el punto de venta con todas sus características
- Puedes usarlo en la nube o en red local; ya no guarda los datos en el navegador
- Tiene gestor de negocios para facilitar su renta
En este tutorial solamente voy a tocar aspectos técnicos sobre su instalación en un servidor Linux sobre todos los pasos que hay que seguir.
Clonar repositorio
Clonar el repositorio, revisar si estás en rama correcta.
Haz go mod tidy por las dudas
Configuración de archivo de entorno
Configura el archivo .env copiando el .env.example
- Para
CLAVE_JWTusahead -c 32 /dev/urandom | xxd -p -u | tr -d '\n' - Para
CLAVE_ENCRIPTACION_BDusahead -c 32 /dev/urandom | xxd -p -u | tr -d '\n' - Para
CLAVE_ENCRIPTACION_RESPALDOSusaopenssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32 - En
Entornoescribeproduction - Finalmente en
PUERTO_SERVIDORelige un puerto desocupado. Recuerda que vas a configurar el proxy a este puerto más adelante. En mi caso es :8205
Primera compilación
Ejecuta make
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ make
gofmt -w .
go build -tags "json1" -ldflags "-X 'main.fechaCompilacion=2026-09-18 15:54:55 UTC' -s -w" -o api_spos4.exe
./api_spos4.exe
Escuchando en :8205. Presiona CTRL + C para salir
Ya viste que ya compiló, que no hubo problemas con el puerto. Detenlo
y asegúrate de conocer la ruta actual con pwd
Nota importante aquí tiene la extensión .exe así que así debes ponerla. No importa, es porque el makefile lo compila así. Solo fíjate en el nombre del ejecutable que aparece arriba de Escuchando en :
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ pwd
/home/parzibyte/spos4_prod/api
Entonces es /home/parzibyte/spos4_prod/api y el ejecutable que hizo
make está en /home/parzibyte/spos4_prod/api/api_spos4.exe
Servicio de systemd
Vamos a crear un servicio y probar. Entonces:
sudo vim /etc/systemd/system/spos4_prod.service
Y le ponemos lo siguiente. La ruta que he venido mencionando
es importante porque debemos ponerla en el WorkingDirectory.
Lo mismo para el ExecStart, que es WorkingDirectory mas el ejecutable
[Unit]
Description=Sublime POS 4 producción
After=network.target
[Service]
User=parzibyte
Group=parzibyte
# Ruta al directorio donde está el servidor de Go
WorkingDirectory=/home/parzibyte/spos4_prod/api
# Comando para iniciar la aplicación
ExecStart=/home/parzibyte/spos4_prod/api/api_spos4.exe
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
Guardamos. Se llama spos4_prod.service así que lo vamos a activar e
iniciar. No es necesario ponerle el .service
sudo systemctl daemon-reload
sudo systemctl enable spos4_prod
sudo systemstl start spos4_prod
En mi caso:
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl daemon-reload
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl start spos4_prod
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl status spos4_prod
● spos4_prod.service - Sublime POS 4 producción
Loaded: loaded (/etc/systemd/system/spos4_prod.service; enabled; preset: disabled)
Active: active (running) since Fri 2026-09-18 10:12:56 CST; 6s ago
Main PID: 1820933 (api_spos4.exe)
Tasks: 6 (limit: 48900)
Memory: 9.2M (peak: 9.5M)
CPU: 56ms
CGroup: /system.slice/spos4_prod.service
└─1820933 /home/parzibyte/spos4_prod/api/api_spos4.exe
Sep 18 10:12:56 vmi2711835.contaboserver.net systemd[1]: Started Sublime POS 4 producción.
Debe decir running y todo bonito. Ahora simplemente probamos ya para pasar al siguiente
punto. Hacemos un curl a la fecha de compilación.
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ curl http://localhost:8205/fecha_compilacion
"2026-09-18 15:54:55 UTC"
Aquí 8205 es el puerto configurado en .env
Proxy de Nginx
Pasemos a configurar Nginx. Haremos que redireccione una ruta a nuestro puerto 8205
sudo vim /etc/nginx/conf.d/default.conf
Dentro de server añadimos:
location /apps/pos2/{
# Dejar que Go maneje el tamaño máximo pero poner un buen límite acá
client_max_body_size 100M;
proxy_pass http://localhost:8205/;
}
Recuerda que ahora apps/pos2 debe estar configurado en el lado del cliente para la API. Más de eso en un momento.
Checamos sintaxis y si todo va bien lo recargamos
sudo nginx -t
sudo systemctl reload nginx
En mi caso:
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl reload nginx
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$
Como yo uso parzibyte.me y configuré /apps/pos2 entonces
puedo probar accediendo a la API con https://parzibyte.me/apps/pos2/
La primera vez (porque no has configurado dist) te debe dar un 404 pero es un 404 propio de Go. Probemos que la API funciona igualmente consultando la fecha de compilación como lo hicimos con cURL hace un momento pero ahora lo haremos desde el navegador a Nginx. Naveguemos a
https://parzibyte.me/apps/pos2/fecha_compilacion
Te debe dar la misma fecha de compilación que te dio al hacer cURL. Si pasa eso entonces ya quedó; solo resta compilar el lado del cliente, subirlo con rsync a la carpeta dist y listo
Subiendo lado del cliente
Antes de salir de la terminal y solo porque no sé qué pasa si no lo haces, crea la carpeta dist que debe ser hermana del ejecutable.
mkdir dist
Y recuerda siempre la ruta absoluta a dist ya que la necesitamos más adelante. En mi caso:
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ mkdir dist
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ cd dist
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api/dist]$ pwd
/home/parzibyte/spos4_prod/api/dist
Es /home/parzibyte/spos4_prod/api/dist
y la ruta de la API es https://parzibyte.me/apps/pos2 (quita la / del final)
Configuremos .env.production de JS
VITE_URL_API=https://parzibyte.me/apps/pos2
Compilamos (en la carpeta raíz, no en la carpeta api):
npm run build
Si todo va bien entonces hacemos rsync con dry run para evitar errores
rsync -rvniz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
En mi caso la salida es:
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$ rsync -rvniz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
sending incremental file list
<f+++++++++ favicon.ico
<f+++++++++ index.html
<f+++++++++ logo.svg
<f+++++++++ manifest.json
cd+++++++++ assets/
<f+++++++++ assets/index-rE_mlUUX.js
<f+++++++++ assets/index-uds7SAWG.css
<f+++++++++ assets/ping-82822-md4el-YO.mp3
<f+++++++++ assets/wrong-47985-pJgIJXG3.mp3
cd+++++++++ images/
<f+++++++++ images/escritorio.png
<f+++++++++ images/inventario.png
<f+++++++++ images/pos.png
<f+++++++++ images/pos_móvil.png
sent 509 bytes received 60 bytes 126,44 bytes/sec
total size is 2.113.031 speedup is 3.713,59 (DRY RUN)
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$
Como vi que la simulación fue exitosa vuelvo a ejecutarlo pero sin dry run o sea quito la n
rsync -rviz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
Visito de nuevo https://parzibyte.me/apps/pos2/ ya sin fecha_compilación y debe darme la webapp
Con esto ya casi tienes todo pero falta generar la licencia y todo eso. Podemos comprobarlo al registrar un negocio e ingresar porque no van a aparecer las sucursales (de hecho deberíamos manejar también ese error)
Generando licencia
Vamos a generar la licencia. Primero compilamos el registrador que está en el mismo código y hacemos que nos dé el machineid del servidor. Entonces:
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api/dist]$ cd ..
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ cd ..
parzibyte@servidor-de-parzibyte.com[~/spos4_prod]$ cd registrador/
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/registrador]$ ls
go.mod go.sum main.go
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/registrador]$ go build
go: downloading go1.26.0 (linux/amd64)
go: downloading github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/registrador]$ ls
go.mod go.sum main.go registrador_spos4
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/registrador]$ ./registrador_spos4
=== REGISTRADOR DE MÁQUINA BY PARZIBYTE ===
MachineID original: hunter2hunter2hunter2
Puedes escanear el siguiente código QR con el MachineID cifrado:
Esto obviamente no crea ningún servidor ni nada, solo es un simple ejecutable que puedes abrir para tener el machineid. Guarda ese machine id, luego ejecuta el firmador y pon dicho machineid, el id de cliente y las fechas.
Recuerda que la clave pública y privada debieron ser generadas antes, y que la pública va en verificador_licencia.go
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4/firmador
$ ./firmador_spos4.exe
Introduce el MachineID: hunter2hunter2
Introduce ID de usuario/cliente: 1
Fecha inicio [Default hoy UTC: 2026-09-18]:
Introduce fecha fin (YYYY-MM-DD): 2027-09-18
1__2026-09-18__2027-09-18__estaeslafirmablablablablasísísí
¡Licencia generada!
Lo copias y lo pegas en un firma.txt que debe ser hermano del ejecutable. Ahora finalmente
consulta a /detalles_licencia:
https://parzibyte.me/apps/pos2/detalles_licencia
Y debería decirte algo como:
{"idCliente":"1","fechaInicio":"2026-09-18","fechaFin":"2027-09-18","machineId":"hola"}
Nota para Parzibyte del futuro: ya que el sistema tiene el machineid podemos tener un endpoint para únicamente el machineid que no se vea afectado por la licencia, así evitamos tener que compilar el registrador por cada cliente
Actualizando sistema
Recomiendo preparar primero el lado del servidor y luego el lado del cliente.
Lado del servidor
Vamos a hacer que el servicio se detenga por el menor tiempo posible, así que lo vamos a detener hasta haber compilado la versión anterior.
Ingresamos a la carpeta api, descargamos cambios con git pull, borramos
ejecutable anterior (no te preocupes, seguirá funcionando),
compilamos con make y luego reiniciamos el servicio.
Puede que make dé un error al ejecutar porque el puerto ya está ocupado. Tal vez en el futuro modifique para que make solo compile, pero mientras tanto el hecho de que aparezca el error de que la dirección ya está ocupada significa que fue compilado correctamente.
cd spos4_prod/api/
git pull
rm api_spos4.exe
make
sudo systemctl restart spos4_prod
En mi caso:
parzibyte@servidor-de-parzibyte.com[~]$ cd spos4_prod/
parzibyte@servidor-de-parzibyte.com[~/spos4_prod]$ cd api
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ git pull
remote: Enumerating objects: 77, done.
remote: Counting objects: 100% (77/77), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 49 (delta 41), reused 37 (delta 29), pack-reused 0 (from 0)
Unpacking objects: 100% (49/49), 12.70 KiB | 151.00 KiB/s, done.
From github.com:parzibyte/spos4
5ff40cb..8330d62 api -> origin/api
Updating 5ff40cb..8330d62
Fast-forward
.env | 2 +-
.env.production | 4 +-
README.md | 286 ++++++++-
api/bd.go | 9 -
api/controlador_ajustes.go | 61 +-
api/controlador_servicios.go | 2 +-
api/handlers_ajustes.go | 10 +
api/main.go | 1 +
api/mapa_rutas_permisos.go | 1 +
src/Clases.ts | 109 ++++
src/components/Negocios/CrearNegocio.vue | 37 +-
src/components/PuntoDeVenta/DialogoVentaTerminada.vue | 3 +-
src/components/Usuarios/Login.vue | 11 +-
src/stores/db.ts | 2 +
src/stores/tickets.ts | 2745 ++++++-------------------------------------------------------------------------------
src/translations.ts | 9 +-
src/views/AcercaDe.vue | 2 +-
src/views/Ajustes/AjustesView.vue | 33 +-
18 files changed, 709 insertions(+), 2618 deletions(-)
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ rm api_spos4.exe
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ make
gofmt -w .
go build -tags "json1" -ldflags "-X 'main.fechaCompilacion=2026-09-19 17:19:08 UTC' -s -w" -o api_spos4.exe
./api_spos4.exe
Escuchando en :8205. Presiona CTRL + C para salir2026/09/19 11:19:22 listen tcp :8205: bind: address already in use
make: *** [makefile:14: run] Error 1
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl restart spos4_prod
[sudo] password for parzibyte: hunter2
parzibyte@servidor-de-parzibyte.com[~/spos4_prod/api]$ sudo systemctl status spos4_prod
● spos4_prod.service - Sublime POS 4 producción
Loaded: loaded (/etc/systemd/system/spos4_prod.service; enabled; preset: disabled)
Active: active (running) since Sat 2026-09-19 11:20:38 CST; 11s ago
Main PID: 1825397 (api_spos4.exe)
Tasks: 6 (limit: 48900)
Memory: 4.0M (peak: 4.6M)
CPU: 50ms
CGroup: /system.slice/spos4_prod.service
└─1825397 /home/parzibyte/spos4_prod/api/api_spos4.exe
Sep 19 11:20:38 vmi2711835.contaboserver.net systemd[1]: Started Sublime POS 4 producción.
Lado del cliente
Es lo mismo que hicimos la primera vez que lo subimos; solo hay que compilar
con npm run build y luego sincronizar con rsync
Entonces:
npm run build
rsync -rvniz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
# Si la salida anterior te parece correcta hazlo sin dry run
rsync -rviz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
De nuevo, en mi caso sale así:
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$ npm run build
> spos4@0.0.0 build
> run-p type-check "build-only {@}" --
> spos4@0.0.0 type-check
> vue-tsc --build --force
> spos4@0.0.0 build-only
> vite build
vite v5.0.12 building for production...
transforming (2) vite\modulepreload-polyfill.jsBrowserslist: caniuse-lite is outdated. Please run:
npx update-browserslist-db@latest
Why you should do it regularly: https://github.com/browserslist/update-db#readme
✓ 1159 modules transformed.
dist/index.html 2.79 kB │ gzip: 1.27 kB
dist/assets/ping-82822-md4el-YO.mp3 4.42 kB
dist/assets/wrong-47985-pJgIJXG3.mp3 5.52 kB
dist/assets/index-TmHVWOg-.css 24.89 kB │ gzip: 5.06 kB
dist/assets/index-HsgfTG-P.js 967.88 kB │ gzip: 277.01 kB
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 24.61s
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$ rsync -rvniz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
sending incremental file list
<f..T...... favicon.ico
<f..T...... index.html
<f..T...... logo.svg
<f..T...... manifest.json
*deleting assets/index-uds7SAWG.css
*deleting assets/index-rE_mlUUX.js
<f+++++++++ assets/index-HsgfTG-P.js
<f+++++++++ assets/index-TmHVWOg-.css
<f..T...... assets/ping-82822-md4el-YO.mp3
<f..T...... assets/wrong-47985-pJgIJXG3.mp3
<f..T...... images/escritorio.png
<f..T...... images/inventario.png
<f..T...... images/pos.png
<f..T...... images/pos_móvil.png
sent 496 bytes received 111 bytes 173,43 bytes/sec
total size is 2.053.499 speedup is 3.383,03 (DRY RUN)
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$ rsync -rviz --delete dist/ parzibyte@servidor-de-parzibyte.com:/home/parzibyte/spos4_prod/api/dist/
sending incremental file list
<f..T...... favicon.ico
<f..T...... index.html
<f..T...... logo.svg
<f..T...... manifest.json
*deleting assets/index-uds7SAWG.css
*deleting assets/index-rE_mlUUX.js
<f+++++++++ assets/index-HsgfTG-P.js
<f+++++++++ assets/index-TmHVWOg-.css
<f..T...... assets/ping-82822-md4el-YO.mp3
<f..T...... assets/wrong-47985-pJgIJXG3.mp3
<f..T...... images/escritorio.png
<f..T...... images/inventario.png
<f..T...... images/pos.png
<f..T...... images/pos_móvil.png
sent 282.460 bytes received 9.431 bytes 53.071,09 bytes/sec
total size is 2.053.499 speedup is 7,04
parzibyte@LAPTOP-3T1ON1MO MSYS /c/Users/parzibyte/Documents/desarrollo/vue/spos4
$
Y no te preocupes, no hay necesidad de reiniciar el servicio ni el servidor cuando haces cambios del lado del cliente pues los archivos se sirven frescos