Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Start


Revisit your travel memories

Open source travel documentation app for self-hosting or serverless usage
Lightweight and future proof


Try out the online version using example data or your own data.


Background

The idea for this application came to me while reflecting on the countless journeys I had taken. Despite all the places I had visited, I found myself forgetting key moments and locations. Friends would ask about my travels, and I often struggled to recall the details or find the photos that matched those memories. I realized I needed a way to not only document my trips but also retrieve the content of the documentation with ease.

That is how Immer in Bewegung was born. The name, meaning always in movement in German, perfectly encapsulates the heart of this project. To bring this concept to life, the frog is chosen as the mascot—a symbol of endless motion and adventure.

Note

You can check out the online app running at https://online.bewegung.app/. If you want to bind your own data, you can do so via https://online.bewegung.app/?path=source.

Basic idea

Over time, most code in applications becomes obsolete. To ensure your memories are not lost to such obsolescence, the core idea of this project is to visualize travel data stored in SQLite format. SQLite databases are easy to update using any of the many available tools, offer long-term reliability, and are more likely to remain compatible with future technologies. This makes it a solid, future-proof solution. However, scrolling through a database directly can be tedious—so why not build an interface on top? But this project is not just an application; it’s a versatile syntax that allows you to take travel notes in a simple and structured way.

Main features

  • A single SQLite database file with a few easy-to-understand tables
  • Use directly on GitHub or host it on your own server
  • Use in conjunction with Immich to combine your documentation with photos (optional)

img Made in Sweden

Quick Start

Usage Combinations

Serverless usageSelf-hosting
ConfigurationVia settings in database fileVia settings in database file
DatabaseYour own database file that you bind via the browser File APIYour own database file that you bind via the browser File API or database file stored on a server

Serverless usage (simplest choice)

Download the database template (read more about how to edit the file under Create the dataset

https://github.com/plans-coding/immer-in-bewegung/raw/refs/heads/main/personal/bewegung.sqlite

To bind your own SQLite file to the online application, go to

https://online.bewegung.app/?path=source

Afterwards just start browsing your trips at

https://online.bewegung.app/

Host on your own server

If you want your app available from many devices without need to bind it specifillay on each device, you can develop your instance to your own server instead.

Step 1 − Download files

Go to the directory where you want to place your files, e.g.

cd /var/www/iib

Download the latest release and extract to the folder

Step 2 − Edit settings

Specify your settings and the path to the database file in server_db_path.txt. Read more at Settings.

Create the dataset

Database Structure

TableTypeDescription
bewa_OverviewTableThe table that defines the extent of your trips. One trip per row.
bewb_EventsTableThe table containg detailed notes from your trips. One row per day (date).
bewx_SettingsTableWhere the settings for the app is stored.

For more detailed instructions, follow the Trip Syntax.

Tip

Download a pre-filled SQLite template file and edit the tables within.

Note

To obtain coordinates for a given position, you can use the Coordinate Tool and easily copy and paste the coordinates into the SQLite file.

Trip Syntax

Table: Overview

The first table in your SQLite file is named bewa_Overview with fields according to following.

Note

Regarding validation: Some fields are checked for syntax and must comply to that.

ColumnDescription
InnerIdThe InnerId is used to connect each row to date notes in table bewb_Events. It consists of a six characters long string generated randomly in Sqlite with lower(hex(randomblob(3)))

Syntax: <xxxxxx>
ParticipantGroupCould e.g. be Family, Private, or Work.

Syntax: <string>
OuterIdThe OuterID is exposed to end-user of the web app. Normally you can take the first letter of the TripDomain and the first letter of ParticipantGroup and add a chronological number.

Syntax: <letters and dashes and serial numbers of your choice>
OverallDestinationType in you trip primary destination. E.g. Finland, or Italia, Spain etc.

Syntax: <string>
DepartureDateYour departure date in ISO format.

Syntax: YYYY-MM-DD
ReturnDateYour return date in ISO format.

Syntax: YYYY-MM-DD
TripLabelsAdd keywords to the trip for filter purposes.

Syntax: LabelA, LabelB, LabelC, ...
MapPinsThe main stops of your trip that you want to plot on the map.

Syntax (multi-line accepted): [ NAME_OF_MAP_PIN ]( LATITUDE, LONGITUDE )
TripDescriptionA short description that explain the aim of the trip. E.g. My fantastic summer trip to France.

Syntax: <string>
PhotoStarttimeFor use with Immich. If you leave your home at let say 8 pm and don’t want to include photos from earlier on departure day, then you can set a time here. If left empty, all photos from departure day will be included.

Syntax: HH:MM
PhotoEndtimeFor use with Immich. Same as above, but for return day.

Syntax: HH:MM

Table: Events

The second table in your SQLite file is named bewb_Events with fields according to following.

Note

Regarding validation: Some fields are checked for syntax and must comply to that.

Column NameDescription
InnerIdReference to corresponding InnerId in table bewa_Overview.

Syntax: <xxxxxx>
DateYour date in ISO format.

Syntax: YYYY-MM-DD
EventsThis is your fully description of what you did this day.

Syntax (multi-line accepted): <string>
AccommodationThe name and the address of your accommodation.

Syntax: <string>
AccommodationCountryThe name of the country you stayed in during the night.

Syntax: <string>
AccommodationCoordinateAccuracyPossibility to mark the accuracy of the coordinates.

Syntax: <string>
AccommodationCoordinatesThe gps coordinates of your accommodation, e.g. 59.329444, 18.068611.

Syntax: lat_decimal,lng_decimal
TravelParticipantsThe name of your travel buddies, separated by comma. N.B. Not exposured in web app.

Syntax: <string>
AdditionalNotesN.B. Additional notes not exposured in web app.

Syntax (multi-line accepted): <string>
CountriesDuringDayThis field is for enhanced statistics. Enter all countries in cronologial order during that day, separated by comma. The country names need to conform to the country names defined in ContinentCountries. In front of the country name prefix *, **, and + is allowed. Read more under Special syntax used for Countries During Day (Events).

Syntax (prefix allowed): <country name without space>, <country name without space>, <country name without space> [...]

Special syntax used for Countries During Day (Events)

PrefixFunction
*Shorter visit of significant importance
**Very short visit of without significant importance
+Restore, counts only if * and ** count

Note

Countries During Day: When a prefix is used in front of a country name in field CountriesDuringDay in table bewb_Events it affect the Statistics page.

Settings

Path to database

Change the relative path to the database file in server_db_path.txt if you want your own database to load automatically when self-hosting.

Database file

The settings is configured the database file table bewx_Settings. The following fields are applicable

Attribute GroupAttributeValueDescription
BaseHomeCountryCountryUse this to remove your own country from the trip unique countries field. Use same spelling as in your notes and in the value of ContinentCountries.
BaseLanguageFileFilenameFilename to the translation file located in the personal folder, e.g. swedish.json.
DefinitionContinentCountriesListDefinitions of continents and countries as well as their language (and spelling).
DefinitionTripDomainColorsListColor definitions of your trip domains.
BaseImmichDisabled or EnabledEnable if you want filter links from all event dates to Immich.
PhotosImmichApiKeyKey
PhotosImmichCoverAlbumIdGUIDThe GUID of the cover photo album in Immich.
FeatureImmichUrlURLOnline URL to the Immich installation (e.g. https://immich.example.com/).
OtherDatasetDisabled or EnabledIf enabled, the link to Dataset is shown.
OtherExternalMapProviderURLSet your prefered map provider.

Trip Domains

There are three pre-defined trip domains

  • Domestic trip
  • Trip abroad
  • Attachment trip

An attachment trip is defined as a visit to a country where you have a deeper connection. For example, if you study abroad, you might want to document that time but distinguish it from the ordinary abroad or domestic category.

Set theme colors to your different TripDomains.

Domestic:#0b5394
Abroad:#1d655e
Attatchment:#C60C30

Continent Countries

If you want to change (make other country definitions or translate country names) the pre-defined country settings you can do it by changing in the country definitions. Only countries defined in the table ContinentCountries can be used in your trip notes and only with the very exact spelling.

Tip

Other language: If you have the countries in bewb_Events (CountriesDuringDay) written in another languange than English, then you can change the app behaviour by translating the countries in ContinentCountries to your own language.

Europe:Albania:AL
Europe:Andorra:AD
Europe:Belgium:BE
Europe:Bosnia-and-Herzegovina:BA
Europe:Bulgaria:BG
Europe:Cyprus:CY
Europe:Cyprus-Northern-Cyprus
Europe:Denmark:DK
Europe:Denmark-Faraoe-Islands:FO
Europe:Estonia:EE
Europe:Finland:FI
Europe:Finland-Åland:AX
Europe:France:FR
Europe:Georgia:GE
Europe:Greece:GR
Europe:Ireland:IE
Europe:Iceland:IS
Europe:Italy:IT
Europe:Kosovo:XK
Europe:Croatia:HR
Europe:Latvia:LV
Europe:Liechtenstein:LI
Europe:Lithuania:LT
Europe:Luxembourg:LU
Europe:Malta:MT
Europe:Moldova:MD
Europe:Moldova-Transnistria
Europe:Monaco:MC
Europe:Montenegro:ME
Europe:Netherlands:NL
Europe:North-Macedonia:MK
Europe:Norway:NO
Europe:Poland:PL
Europe:Portugal:PT
Europe:Romania:RO
Europe:Russia:RU
Europe:San-Marino:SM
Europe:Switzerland:CH
Europe:Serbia:RS
Europe:Slovakia:SK
Europe:Slovenia:SI
Europe:Spain:ES
Europe:United-Kingdom:GB
Europe:United-Kingdom-Akrotiri-and-Dhekelia:GB
Europe:United-Kingdom-Gibraltar:GI
Europe:United-Kingdom-Jersey:JE
Europe:United-Kingdom-Northern-Ireland:GB
Europe:Sweden:SE
Europe:Czech-Republic:CZ
Europe:Germany:DE
Europe:Ukraine:UA
Europe:Hungary:HU
Europe:Vatican:VA
Europe:Belarus:BY
Europe:Austria:AT
Africa:Algeria:DZ
Africa:Angola:AO
Africa:Benin:BJ
Africa:Botswana:BW
Africa:Burkina-Faso:BF
Africa:Burundi:BI
Africa:Cabo-Verde:CV
Africa:Central-African-Republic:CF
Africa:Democratic-Republic-of-the-Congo:CD
Africa:Djibouti:DJ
Africa:Egypt:EG
Africa:Equatorial-Guinea:GQ
Africa:Ivory-Coast:CI
Africa:Eritrea:ER
Africa:Eswatini:SZ
Africa:Ethiopia:ET
Africa:Gabon:GA
Africa:Gambia:GM
Africa:Ghana:GH
Africa:Guinea:GN
Africa:Guinea-Bissau:GW
Africa:Cameroon:CM
Africa:Kenya:KE
Africa:Comoros:KM
Africa:Lesotho:LS
Africa:Liberia:LR
Africa:Libya:LY
Africa:Madagascar:MG
Africa:Malawi:MW
Africa:Mali:ML
Africa:Morocco:MA
Africa:Mauritania:MR
Africa:Mauritius:MU
Africa:Mozambique:MZ
Africa:Namibia:NA
Africa:Niger:NE
Africa:Nigeria:NG
Africa:Republic-of-the-Congo:CG
Africa:Rwanda:RW
Africa:Senegal:SN
Africa:Seychelles:SC
Africa:Sierra-Leone:SL
Africa:Somalia:SO
Africa:Sudan:SD
Africa:South-Africa:ZA
Africa:South-Sudan:SS
Africa:Sao-Tome-and-Principe:ST
Africa:Tanzania:TZ
Africa:Chad:TD
Africa:Togo:TG
Africa:Tunisia:TN
Africa:Uganda:UG
Africa:Zambia:ZM
Africa:Zimbabwe:ZW
Asia:Afghanistan:AF
Asia:Armenia:AM
Asia:Azerbaijan:AZ
Asia:Bahrain:BH
Asia:Bangladesh:BD
Asia:Bhutan:BT
Asia:Brunei:BN
Asia:Cyprus:CY
Asia:Cyprus-Northern-Cyprus
Asia:Philippines:PH
Asia:United-Arab-Emirates:AE
Asia:Georgia:GE
Asia:India:IN
Asia:Indonesia:ID
Asia:Iraq:IQ
Asia:Iran:IR
Asia:Israel:IL
Asia:Japan:JP
Asia:Yemen:YE
Asia:Jordan:JO
Asia:Cambodia:KH
Asia:Kazakhstan:KZ
Asia:China:CN
Asia:Kyrgyzstan:KG
Asia:Kuwait:KW
Asia:Laos:LA
Asia:Lebanon:LB
Asia:Malaysia:MY
Asia:Maldives:MV
Asia:Mongolia:MN
Asia:Myanmar:MM
Asia:Nepal:NP
Asia:North-Korea:KP
Asia:Oman:OM
Asia:Pakistan:PK
Asia:Qatar:QA
Asia:Russia:RU
Asia:Saudi-Arabia:SA
Asia:Singapore:SG
Asia:Sri-Lanka:LK
Asia:South-Korea:KR
Asia:Syria:SY
Asia:Tajikistan:TJ
Asia:Taiwan:TW
Asia:Thailand:TH
Asia:Timor-Leste:TL
Asia:Turkey:TR
Asia:Turkmenistan:TM
Asia:Uzbekistan:UZ
Asia:Vietnam:VN
North-America:Antigua-and-Barbuda:AG
North-America:Bahamas:BS
North-America:Barbados:BB
North-America:Belize:BZ
North-America:Costa-Rica:CR
North-America:Denmark-Greenland:GL
North-America:Dominica:DM
North-America:Dominican-Republic:DO
North-America:El-Salvador:SV
North-America:Grenada:GD
North-America:Guatemala:GT
North-America:Haiti:HT
North-America:Honduras:HN
North-America:Jamaica:JM
North-America:Canada:CA
North-America:Cuba:CU
North-America:Mexico:MX
North-America:Nicaragua:NI
North-America:Panama:PA
North-America:Saint-Kitts-and-Nevis:KN
North-America:Saint-Lucia:LC
North-America:Saint-Vincent-and-the-Grenadines:VC
North-America:Trinidad-and-Tobago:TT
North-America:USA:US
Oceania:Australia:AU
Oceania:Fiji:FJ
Oceania:Kiribati:KI
Oceania:Marshall-Islands:MH
Oceania:Micronesia:FM
Oceania:Nauru:NR
Oceania:New-Zealand:NZ
Oceania:Palau:PW
Oceania:Papua-New-Guinea:PG
Oceania:Solomon-Islands:SB
Oceania:Samoa:WS
Oceania:Tonga:TO
Oceania:Tuvalu:TV
Oceania:Vanuatu:VU
South-America:Argentina:AR
South-America:Bolivia:BO
South-America:Brazil:BR
South-America:Chile:CL
South-America:Colombia:CO
South-America:Ecuador:EC
South-America:Guyana:GY
South-America:Paraguay:PY
South-America:Peru:PE
South-America:Suriname:SR
South-America:Uruguay:UY
South-America:Venezuela:VE

Translations

App translation

If the app is not available in your language you can easily translate it by placing a file in the folder personal/ and change the value in settings table bewx_Settings.

Read more under Settings.

Continent and Country translations

The continent and countries can be translated, read more Continent Countries.

Trip Domain translation

The trip domains can be translated, read more Trip Domains.

Immich Integration

Tip

When Immer in Bewegung embeds photos in image view (see below), the search bar from Immich will be displayed unless you use small a css modification in your Immich settings.

Add

body:has(#search-chips:target) #asset-selection-app-bar,
body:has(#search-chips:target) #search-chips {
  display:none;
}
body:has(#search-chips:target),
body:has(#search-chips:target) #search-content {
  background-color: #000000;
}

to User Icon > Admininstration > Settings > Theme Settings: Custom CSS.

img

Usage of Immich API

To make use of Immich API you need to allow CORS, example snippet to add to /etc/caddy/Caddyfile

(cors) {
        @cors_preflight{args[0]} method OPTIONS
        @cors{args[0]} header Origin {args[0]}

        handle @cors_preflight{args[0]} {
                header {
                        Access-Control-Allow-Origin "{args[0]}"
                        Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
                        Access-Control-Allow-Headers *
                        Access-Control-Max-Age "3600"
                        defer
                }
                respond "" 204
        }

        handle @cors{args[0]} {
                header {
                        Access-Control-Allow-Origin "{args[0]}"
                        Access-Control-Expose-Headers *
                        defer
                }
        }
}

immich.your-server-name.dedyn.io {

        # Needed for API call
        import cors https://immich.your-server-name.dedyn.io

        # Needed for iframe embedding
        header {
                Cross-Origin-Embedder-Policy require-corp
                Cross-Origin-Resource-Policy cross-origin
        }

        @options method OPTIONS
        respond @options 204

        reverse_proxy http://localhost:2283

}

Other needs

Expose web server to internet

Using Caddy this becomes easy. Deploy the server with

immich.your-server-name.dedyn.io {

        # Generate password storing hash with: caddy hash-password
        basic_auth {
                # Username "iib", password "bewegung"
                iib $2a$14$basROD3Y0cLE.VqXd.h89.akCQDKzhp6IH9ND2CRFyEICkMrKn3AO        
        }

        root /var/www/iib/

}

Alternatives

  • Add support for https
    • E.g. via Let’s encrypt
  • Use user authentication via
    • Authelia, or
    • Authentik, or
    • Client certificates, or
    • Port forwarding over SSH (e.g. using Termius on Android)

List over SQL Queries

You can browse the folder src/queries/ if you want to see what SQL queries the app is using. This can be useful if you want to make some analytics in another other software.

Logotype

If you like Immer in Bewegung and want to link to the project from your homepage, you can use this button.

img

Code for embedding to copy

<a href="https://bewegung.app/" style="text-decoration:none;"><img src="img/get_bewegung.svg"></a>